Pure Nix
Actors are functions. State is a closure. Pipelines are let bindings.
# A behaviour is a plain Nix functioncounter = count: msg: if msg == "inc" then reply.right (count + 1) // become (counter (count + 1)) else if msg == "get" then reply.right count else { }; # unknown message → no reply, no state change
counter-c = actor (counter 0);
result = counter-c { inbox = st "inc" "inc" "get"; };result.outbox.toList# → [ { right = 1; } { right = 2; } { right = 2; } ]Wire actors by passing outbox as inbox. Nix laziness resolves evaluation order.
# Scatter: validate N inputs in parallel; gather: merge resultsinputs = [ 5 (-1) 8 0 2 ];actors = builtins.map (n: validator-c { inbox = st n; }) inputs;results = merge (builtins.map (a: a.outbox) actors);
results.toList# → [ { right = 5; } { left = "non-positive: -1"; } { right = 8; }# { left = "non-positive: 0"; } { right = 2; } ]# Ping-pong via lazy let — pong feeds pingpong = counter-c { inherit inbox; };ping = counter-c { inbox = pong.outbox (st.map (_: "get")); };# Either split: route successes and errors to separate streamsvalidated = validator-c { inbox = st 5 (-1) 8 0 2; };validated.outbox.right.toList # → [ 5 8 2 ]validated.outbox.left.toList # → [ "non-positive: -1" "non-positive: 0" ]A cycle-c is a Nix value. Return it as a reply — the receiver gains the ability to invoke that actor.
# Server vends fresh counter sessions on connectsession-server-c = actor (_: reply counter-c);
session-refs = (session-server-c { inbox = st "connect" "connect"; }).outbox;
# Each ref is an independent sessionsessions = session-refs.flatMap (ref: (ref { inbox = st "inc" "inc" "get"; }).outbox);sessions.toList# → [ { right = 1; } { right = 2; } { right = 2; } ← session 1# { right = 1; } { right = 2; } { right = 2; } ] ← session 2Tag replies with client id. Each client filters the shared outbox.
server-c = actor (msg: reply { id = msg.id; result = msg.value * 2; });
server = server-c { inbox = shared-bus; };client-a = server.outbox.filter (r: r.id == "a");client-b = server.outbox.filter (r: r.id == "b");Pure Nix
Actors are functions. State is a closure. Pipelines are let bindings.
Lazy pipelines
Nix laziness resolves actor dependency order. Ping-pong, feedback loops, N-stage pipelines — all let bindings.
Either routing
reply.right / reply.left tag outbox entries. outbox.right and outbox.left split the stream without filtering.
Refs as capabilities
A cycle-c is a value. Return it as a reply. Holding the ref is the permission.