Skip to content

Multi-Output Cycles

A cycle-c can return any attrset. Return multiple named streams instead of a single outbox:

flowchart LR
  inbox["inbox [5,-1,3,0,7]"]
  v["validator-c"]
  inbox --> v
  v -->|"outbox.right"| vals["values\n[5, 3, 7]"]
  v -->|"outbox.left"| errs["errors\n["non-positive: -1", …]"]
validate-c =
{ inbox }:
let a = validator-c { inherit inbox; };
in {
values = a.outbox.right;
errors = a.outbox.left;
};
result = validate-c { inbox = st 5 (-1) 3 0 7; };
result.values.toList
# [ 5 3 7 ]
result.errors.toList
# [ "non-positive: -1" "non-positive: 0" ]

Callers access named outputs directly without knowing about Either internals.

Behaviours can set extra fields that don’t appear in outbox. The states stream carries all per-message state, and states.fields "key" extracts any field:

flowchart LR
  inbox["inbox ["inc","get","inc","get"]"]
  lc["logged-counter-c"]
  inbox --> lc
  lc -->|"outbox"| ob["outbox [1,1,2,2]"]
  lc -->|"states.fields "kind"\n(st.filter "mutation")"| mut["mutations ["mutation","mutation"]"]
  lc -->|"states.fields "kind"\n(st.filter "query")"| qry["queries ["query","query"]"]
logged-counter =
count: msg:
if msg == "inc" then
reply.right (count + 1) // { kind = "mutation"; } // become (logged-counter (count + 1))
else if msg == "get" then
reply.right count // { kind = "query"; }
else
{ };
lc = actor (logged-counter 0);
lc-c =
{ inbox }:
let a = lc { inherit inbox; };
in {
mutations = a.states.fields "kind" (st.filter (k: k == "mutation"));
queries = a.states.fields "kind" (st.filter (k: k == "query"));
outbox = a.outbox;
};
result = lc-c { inbox = st "inc" "get" "inc" "get"; };
result.mutations.toList # [ "mutation" "mutation" ]
result.queries.toList # [ "query" "query" ]

states.fields "kind" extracts the kind field from each state into a ST. Applying st.filter to it filters by value.

Use an extra field to build an audit log alongside normal output:

logged-div =
msg:
if msg.divisor == 0 then
{ reply = { left = "div-by-zero"; }; log = "error: div-by-zero"; }
else
{ reply = { right = msg.dividend / msg.divisor; }; log = "ok: ${toString (msg.dividend / msg.divisor)}"; };
a = (actor logged-div) { inbox = st { dividend = 10; divisor = 2; }
{ dividend = 5; divisor = 0; }; };
a.outbox.toList # [ { right = 5; } { left = "div-by-zero"; } ]
(a.states.fields "log").toList # [ "ok: 5" "error: div-by-zero" ]
Contribute Community Sponsor