Introduction

A Letlang process is a green-thread running a single function to completion (or until it crashes). Every process has a unique identifier (a pid).

Functions have no color, any function can be run in a process. It is up to the caller to decide if a function should be run in a new process or not.

When spawning a new process, its pid is returned, allowing the caller to interact with it.

Example:

func task() -> @ok {
  # do stuff
  @ok;
}

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

  # do stuff

  @ok;
}

About side effects and exceptions

In every process, if a side effect (or an exception) is not intercepted/caught with a do{} expression (see chapter 3), it bubbles up to the Letlang runtime.

Example:

func task() -> @ok {
  throw @oops;
}

func main() -> @ok {
  @ok := do {
    task();  # the exception is caught
  }
  catch @oops {
    @ok;
  };

  spawn task();  # the exception will bubble up to the runtime

  # do stuff

  @ok;
}

When an exception or an unknown effect bubbles up to the runtime, the process crashes, but not the whole program.

How does it work?

When starting, Letlang starts a node which listens for commands:

  • spawning processes
  • linking/unlinking processes
  • sending message to a process
  • notification of process termination

When the node spawns a process, it creates a new PID and a mailbox to exchange signals. If the process terminates normally, or panics, it is caught and the node is notified.

NB: The main function is automatically spawned as a new process (the first one).

When every process are terminated, the node stops and the program terminates. This means that the main function can terminate before the tasks it spawned, it won’t stop the program.