Operators¶
The DSL is a small, deliberately constrained subset of Python. Every construct is validated at definition time; unsupported ones raise a clear error instead of miscompiling.
Lifting: A(x) and lift¶
A(x) (or lift(x, A) / lift(x, {A, B})) lifts an expression to a role:
it says "this value is produced at A". Only A evaluates the expression; the
result is located at {A} (or the given role set).
Communication: copy, narrow, move¶
| Operator | Effect | Result location |
|---|---|---|
copy(x, A, B) |
communicate x from A to B |
loc(x) ∪ {B} |
narrow(x, {A}) |
forget other locations (no communication) | {A} |
move(x, A, B) |
shorthand: narrow(copy(x, A, B), {B}) |
{B} |
copy requires A ∈ loc(x); narrow requires the target set to be a subset
of loc(x). These are checked statically, per role, at definition time:
Communication operands are variables
copy/move/narrow operands must already be choreographic variables
(names). Write the expression first, then move it:
Tuple values: pack and unpack¶
pack(a, b, …) groups located values into a tuple value; unpack([x, y], t)
pulls one apart, binding each element at its own location.
t = pack(x, y) # element 0 located where x is, element 1 where y is
unpack([a, b], t) # a @ loc(x), b @ loc(y)
Each role materializes a zone-filled view of a tuple — real values for the
elements it holds, UNHELD elsewhere — so tuples need no communication to
build or read. See Types & parameters.
Communication stays scalar
copy/move/narrow/lift take scalar (agreement) operands only.
Moving a distributed tuple is not supported: unpack it, move the parts, and
repack. (Each element already lives at its role, so this is rarely needed.)
Role actions: Role.print¶
A.print(x) prints x at role A (useful while simulating).
What's not here (yet)¶
There is no agree! (programmer-declared agreement — the unsafe escape hatch
Klor has), no defchor forward-declaration/mutual recursion, and no
higher-order choreography-as-value types. These are documented in
Limitations.