Skip to content

Processes

A function that receives messages declares them with receives. That mailbox is part of the function type and is invariant: a fn() receives Integer -> :ok is not a fn() receives Term -> :ok, and Pid<Integer> is not a Pid<Term>. receive is legal only inside a receiving function, and its patterns must exhaust the mailbox. gale_std.process.self is accepted only in a receiving function, and the Pid message parameter must match that mailbox.

module processes
pub type Message =
| Ping(gale_std.process.Pid<:pong>)
| Stop
fn loop() receives Message -> :ok {
receive {
Ping(reply_to) -> {
let sent = gale_std.process.send(reply_to, :pong)
loop()
}
Stop -> :ok
}
}
pub fn start() -> gale_std.process.Pid<Message> {
gale_std.process.spawn(fn() receives Message => loop())
}
pub fn round_trip(server: gale_std.process.Pid<Message>) receives :pong -> :pong {
let reply_to = gale_std.process.self()
let sent = gale_std.process.send(server, Ping(reply_to))
receive {
:pong -> :pong
}
}
pub fn widen(pid: gale_std.process.Pid<Integer>) -> gale_std.process.Pid<Term> {
pid
}

A receive pattern can also pin a variable with ^name to match the value it held when the receive started. Tagged replies are the classic use: a request that carries a fresh tag waits for (:reply, ^tag, result) and ignores replies to other requests still in the mailbox. Because the pin is emitted into the BEAM pattern itself, the VM can skip messages that arrived before the receive began — a when guard cannot do that.

fn await(tag: Integer) receives (:reply, Integer, Term) -> Term {
receive {
(:reply, ^tag, result) -> result
(:reply, _, _) -> await(tag)
}
}