LET
lang

status: WIP version: 0.0.0 license: MIT

Letlang is a general purpose functional programming language.

Implemented in Rust, it compiles to Rust, allowing you to target any platform supported by LLVM.

Work in progress

Letlang is in a very early stage.

The documentation and code examples you may find on this website are NOT definitive and may be subject to change.



Dynamic Type System

let even: class[int] {
  (n) -> n % 2 = 0,
};

let odd: class[int & !even];

A value does not have a single type, instead the type definition determines what value it contains.

42 is a number, an int, an even but not an odd.

Pattern Matching

(@ok, val) := (@ok, 42);

(@ok, res) := (@error, @oops); # error

(a, b) := (1, 2);

The := operator is a pattern matching operator, used to bind values to new variables, and extract values from complex data structures.

Lazy Constraints

let (x: int, y: int) with x > y {
  x := 0;
  y := 1; # error
};

let expressions create a new scope where future bindings are constrained to a type and a guard expression.

Actor Based Concurrency

let task: func[() -> @ok] {
  () -> @ok,
};

let pub main: func[() -> @ok] {
  () -> {
    (@ok, proc_id) := spawn task();
    # do something
    @ok;
  },
};

Run any function in a new Letlang process (concurrent task), inspired by Erlang's design, and backed by Tokio.

Your program will live as long as there is a running process even if the main() function returned.

Every process have a unique identifier (pid) used for communication and monitoring.

Message Passing

(@ok, proc_id) := spawn task(std::proc::self());
# send a signal to this process on exit
std::proc::link(proc_id);

# blocks until signal is received
receive {
  (@message, proc_id, msg) -> {
    # message signal received
  },
  (@exited, proc_id, reason) -> {
    # exit signal received
  },
};
@ok := std::proc::send(proc_id, "hello");

Each process has a mailbox where signals can be queued. There are 2 kinds of signals: message and exited.

Processes can send messages to each others. When a process exits (or crashes), an exited signal is sent to every linked process.

Effect Handlers

let log: effect[(string) -> @ok];
do {
  @ok := perform log("hello");
}
intercept log {
  (msg) -> std::io::println(msg),
};

Delegate the handling of side effects to the caller, or let them bubble up to the Letlang runtime.

📰 News Feed