Skip to content

actor

actor : behaviour cycle-c
# where
# behaviour = state → msg → { reply?, next-behaviour?, ...extras }
# cycle-c = { inbox: ST } → { outbox: ST, states: ST }

actor initial-behaviour returns a cycle-c — a function that takes { inbox } and returns { outbox, states }.

flowchart LR
  beh["initial-behaviour\n(Nix function)"]
  actor["actor"]
  cyclec["cycle-c\n{ inbox } → { outbox, states }"]
  beh --> actor --> cyclec

Internally, actor runs scanl over the inbox stream. Each message is passed to the current behaviour; the result is used to:

  1. Extract next-behaviour (if present) as the behaviour for the next message
  2. Carry all other result fields into the accumulated state

outbox is states.fields "reply" — the reply field from each state, flattened into a stream.

ParameterTypeDescription
initial-behaviourstate → msg → attrsetThe first behaviour to run. Subsequent behaviours come from become.
inboxSTStream of messages to process.
FieldTypeDescription
outboxSTStream of reply values from each message that produced one.
statesSTFull scanl output — one state per message plus the initial state.
FieldEffect
replyValue placed in outbox. May be a plain value or a ST (flattened).
next-behaviourReplaces the current behaviour for the next message.
Any other fieldPasses through to states. Accessible via states.fields "fieldname".

Stateless actor:

echo-c = actor (msg: reply msg);
(echo-c { inbox = st "a" "b"; }).outbox.toList
# [ "a" "b" ]

Stateful actor:

counter = count: msg:
if msg == "inc" then reply.right (count + 1) // become (counter (count + 1))
else if msg == "get" then reply.right count
else { };
counter-c = actor (counter 0);
(counter-c { inbox = st "inc" "inc" "get"; }).outbox.toList
# → [ { right = 1; } { right = 2; } { right = 2; } ]

Fan-out (ST reply):

expander-c = actor (msg: reply (st msg msg));
(expander-c { inbox = st "a" "b"; }).outbox.toList
# [ "a" "a" "b" "b" ]
Contribute Community Sponsor