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

class even(n: int) {
  n % 2 = 0;

class odd(n: 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.

Side Effects

effect log(msg: string) -> @ok;
do {
  @ok := perform log("hello");
intercept log(msg) {
Delegate the handling of side effects to the caller, or let them bubble up to the Letlang runtime.

Actor Based Concurrency

func task() -> @ok {
  # do something

func main() -> @ok {
  (@ok, proc_id) := spawn task();
  # do something

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

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

Each process have a mailbox where signals can be queued. There exist 2 kinds of signal: 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.

Immutable & No GC

a := 1;
# rebinding to a new value
# does not mutate the previous value
a := 2;
a := 1;

do {
  a := 2;
  # this binding goes out of scope

# a equals 1

Variables are bound to values during the lifetime of a scope. They can be rebound to new values, but the previous value is not mutated. There is no mutable state, let alone shared mutable state.

Letlang relies on Rust’s ownership semantics to free the memory when objects go out of scope, making it free of any Garbage Collector.

📰 News Feed