A statically-typed, functional, and sound programming language with type inference.


samlang is a statically-typed functional programming language designed and implemented by Sam Zhou. The language is still under development so the syntax and semantics may be changed at any time.

The language can be compiled down to WebAssembly with reference counting based garbage collection.

Getting Started

Download the latest release from GitHub Releases. To start a new project, create a json file sconfig.json with content {}.

Notable Examples

Hello World

class HelloWorld {
  function getString(): Str =
    "Hello World"


class Math {
  function answerToLife(): int =
    2 * 21

Pattern Matching

class Opt<T>(
  None, Some(T)
) {
  method isEmpty(): bool =
    match (this) {
      None -> true,
      Some(_) -> false,

  method <R> map(f: (T) -> R): Opt<R> =
    match (this) {
      None -> Opt.None(),
      Some(v) -> Opt.Some(f(v)),

Type Inference

class TypeInference {
  function <A, B, C> pipe(
    a: A, f1: (A)->B, f2: (B)->C
  ): C = f2(f1(a))

  function main(): unit = {
    // n: int
    // s: string
    let _ = TypeInference.pipe(
      (n) -> Str.fromInt(n),
      (s) -> s.toInt()

About the Documentation Site

This is an interactive documentation site where all the code snippets of samlang are editable. These snippets, available after the introduction section, contain inline comments to illustrate the power of the language.

You can click the Demo button at the top of the page or follow this link.

Power and Limits of Type Inference

The only absolutely required type annotated happens at the top-level class function and method level. Most other types can be correctly inferred by the compiler and can be omitted from your program.

The type checker uses local type inference to infer types of most expressions. Therefore, it cannot infer types from the whole program like OCaml does. Instead, it will push down type hints from local, nearby contexts.

Despite the fundamental limitation, the compiler can correctly infer most of the local expression types. If your code does not use generics or unannotated lambda parameters, then all types can be inferred correctly. Most of the type arguments can be inferred, so they do not need to be explicitly supplied. Unannotated lambda parameters with local context but without parametric polymorphism can also be inferred perfectly.

Even when you combine polymorphic function call and unannotated lambda parameters, the type checker still attempts to infer the types from left to right. It will work in most of the code. An illustratin type inference example is available near the top of the page.