Processes and channels
Goal: create processes with
spawn, make them talk to each other over typed channels, and wait on several at once with the subjectlessmatch(the “select”).
Processes
Section titled “Processes”The unit of concurrency is the process: isolated (its own heap), cheap, managed by the runtime.
You create one with spawn (like Go’s go):
spawn log_metric(x) // fire-and-forget: no cost, no referencespawn worker(data) catch |e| { ... } // with a failure handler (death becomes |e|; see ch. 07)Processes do not share memory (there is no @shared). They talk over a channel.
Typed channels
Section titled “Typed channels”The channel is typed from the perspective of whoever holds the handle: <-T = “I send T”, ->T = “I
receive T”. This eliminates the type inversion in the other side’s signature:
c: Channel[<-int, ->string] // I send int, receive string
spawn worker(c) // the compiler checks that the two sides are complementary
fn worker(c: Channel[<-string, ->int]) { // the worker: sends string, receives int val := -> c // receive (blocks until something arrives) c <- val.to_string() // send (async, fire-and-forget)}Operators: chan <- data (send), data := -> chan (receive), and the sync request-reply with
a mandatory timeout:
reply := server <-> req timeout(5s) catch |e| { return e } // Timeout | ProcessDownThe timeout is mandatory on <-> because two processes doing <-> on each other would deadlock
for sure: the deadline is the structural way out. And note there is no coloring: the <-> only suspends the
current process; the scheduler takes care of the rest.
Waiting on several: subjectless match (select)
Section titled “Waiting on several: subjectless match (select)”The same match, with no value between match and {, waits for the first ready channel:
match { msg := -> chan_a => handle_a(msg) msg := -> chan_b => handle_b(msg)} timeout(100ms) { deu_timeout() // 'timeout' is a trailer of the select; timeout(0) = poll}IO parallelism is by processes
Section titled “IO parallelism is by processes”No Future/await: for N operations in parallel, you spawn N processes and receive the results:
spawn fetch("https://a", chan_a)spawn fetch("https://b", chan_b)ra := -> chan_a // blocks only when necessaryrb := -> chan_bReferencing a process (opt-in)
Section titled “Referencing a process (opt-in)”spawn is fire-and-forget and returns nothing. To inspect a process later, register it
by name (stable across restart): there is no raw pid.
@register("db_writer")spawn db_writer(conn)
p := runtime.process("db_writer") // Optional[Process], by nameme := runtime.self() // the own reference, from inside any processNext: 07 · Resilience