Concurrency & The Actor Model
Turn's concurrency model is inspired by Erlang and Elixir: actors. Every agent is an isolated process with its own environment, context, memory, and mailbox. Actors communicate by sending messages. They cannot share state.
This model makes multi-agent systems composable, fault-tolerant, and scalable by construction.
Every Agent Is a Process
A Turn process has:
- Its own environment (
letbindings) - Its own context window (isolated, token-bounded)
- Its own memory (isolated HNSW graph)
- Its own mailbox (message queue)
- Its own PID (process identifier)
No two processes share any of these. Coordination happens exclusively through message passing.
spawn — Creating Child Agents
let pid = spawn { <program> };Spawns a new concurrent agent process. Returns its PID immediately. The spawned process runs concurrently on the Tokio scheduler.
// Spawn a child agent that waits for a task
let analyst = spawn {
let task = receive();
let result = infer Insight {
"Analyze this business dataset: " + task;
};
return result;
};
call("echo", "Agent spawned with PID: " + analyst);send / receive — Message Passing
Agents communicate by sending values to each other's mailboxes:
send(pid, value); // Non-blocking — puts message in pid's mailbox
let msg = receive(); // Blocks until a message arrives// Spawn a specialized analyst agent
let analyst = spawn {
let query = receive();
return infer Analysis { query; };
};
// Spawn a writer agent that receives analyst output
let writer = spawn {
let analysis = receive();
return infer Report { "Draft an executive summary from: " + analysis.summary; };
};
// Orchestrate: send query to analyst, route result to writer
send(analyst, "Q4 performance data: revenue up 18%, churn up 3%");
let analysis_result = receive(); // analyst sends back to parent
send(writer, analysis_result);
let report = receive();
call("echo", report);NOTE
receive() blocks the current process (not a thread — it yields to the Tokio scheduler). Other agents continue running while the receiving agent waits.
link and monitor — Supervision
Turn supports Erlang-style process linking for fault isolation and supervisor trees.
link(pid) — Bidirectional Crash Propagation
let worker = spawn { ... };
link(worker);
// If worker crashes, the parent receives an EXIT signal in its mailbox
// If the parent crashes, worker receives an EXIT signalWhen a linked process exits with an error, its linked partners receive an EXIT message in their mailboxes. You can choose to handle it or let it propagate — building supervisor trees just like Erlang/OTP.
monitor(pid) — Unidirectional Observation
let worker = spawn { ... };
monitor(worker);
// If worker exits (normally or abnormally),
// this process receives a DOWN message — without crashing itselfmonitor is one-directional: only the monitoring process is notified. Use it when you want observability without fate-sharing.
A Full Multi-Agent Example
The following is a simplified version of Turn's canonical boardroom example — demonstrating a swarm of specialized agents collaborating on a shared task:
struct BoardMemo { recommendation: Str, vote: Str, confidence: Num };
// Spawn three specialist agents
let cfo = spawn {
let brief = receive();
return infer BoardMemo {
"You are the CFO. Evaluate the financial risk of: " + brief;
};
};
let cto = spawn {
let brief = receive();
return infer BoardMemo {
"You are the CTO. Evaluate technical feasibility of: " + brief;
};
};
let cmo = spawn {
let brief = receive();
return infer BoardMemo {
"You are the CMO. Evaluate market opportunity of: " + brief;
};
};
// Distribute the brief
let proposal = "Acquire a 22-person AI startup for $12M to accelerate our inference roadmap.";
send(cfo, proposal);
send(cto, proposal);
send(cmo, proposal);
// Collect votes
let cfo_memo = receive();
let cto_memo = receive();
let cmo_memo = receive();
// Aggregate
call("echo", "CFO: " + cfo_memo.vote + " (confidence: " + cfo_memo.confidence + ")");
call("echo", "CTO: " + cto_memo.vote + " (confidence: " + cto_memo.confidence + ")");
call("echo", "CMO: " + cmo_memo.vote + " (confidence: " + cmo_memo.confidence + ")");Each agent runs concurrently, maintains its own context, performs its own inference, and returns a typed BoardMemo. The orchestrating process collects results without any shared state.
Remote Agents
Turn supports spawning agents on remote nodes over TCP:
let remote_pid = spawn_remote("192.168.1.42:9001", {
let task = receive();
return infer HeavyAnalysis { task; };
});
send(remote_pid, my_dataset);The Turn distributed switchboard routes messages between nodes transparently. From your code's perspective, a remote PID is identical to a local one — the same send/receive API, the same message types.
Next Steps
- Example Agent — Full boardroom simulation with actor coordination
- Runtime Model — How the Tokio scheduler maps actors to threads
- Error Handling — How EXIT signals propagate through linked actors