Summary#
on <Event> { … } inside a state says what happens when that event arrives there. Its body is either one flat body
or a list of ARMS: when (guard) { … } arms, tried top to bottom, and an optional default { … }. An arm usually ends
in goto <State>;. Inside an arm, a when (…) { …; goto X; } may NEST — a branch out of the state.
Signature#
on Event { …; goto Next; } // a flat body: one implicit default arm
on Event(Decision d) {
when (d == Decision.Reject) { goto Rejected; } // arms are tried top to bottom
default {
when (this.Item.Urgent) { Page(); goto Urgent; } // a NESTED when must end in goto
goto Approved;
}
}Description#
Arms#
The arms of a route are its top level. The first when whose guard holds runs; if none does, default runs; if
there is no default, the event changes nothing. An arm's goto is where the run goes next — an arm without one
stays in the state, which is how a route records something and keeps waiting.
A when nested inside an arm#
Inside an arm, when (guard) { …; goto X; } is a branch OUT of the state: if the guard holds, its body runs and the
run goes to X; otherwise the arm carries on. It is exactly if (guard) { …; goto X; }, and that is how an exported
app spells it.
It must end in goto. A nested when that stays in the state would read as one more arm to pick from, and it is
not one — the rest of the enclosing arm runs after it — so it is refused, and the message names both ways to write
what you meant:
- a branch that leaves the state: end it with
goto <State>;; - a step that stays: write
if (…) { … }.
A when outside any on route (in an enter { }, in a function) is not an arm at all; write if.
Why nest at all#
Without it, "urgent work goes to the urgent state, everything else to the queue — but both are accepted first" has to be two top-level arms that repeat the shared part:
on Accept {
when (this.Item.Urgent) { Stamp(); goto Urgent; }
default { Stamp(); goto Queued; }
}Examples#
A whole app, compiled: an urgent job leaves for Urgent from inside the default arm, every other one for Done.
using Osysharp.Workflow;
enum JobStage { Open, Urgent, Done }
entity Job {
[Required, MaxLength(100)] string Title;
bool Urgent;
[MaxLength(100)] string Note = "";
JobStage Stage;
security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}
workflow JobFlow {
Tracks = Job.Stage;
Autostart = true;
Initial = Open;
event Go();
state Open {
on Go {
default {
when (this.Item.Urgent) { this.Item.Note = "paged"; goto Urgent; }
goto Done;
}
}
}
state Urgent {
on Go { goto Done; } // paged — the next Go closes it
}
terminal success Done { }
}
void Start(Job j) { JobFlow.RaiseGo(j); }See also#
- Raising a workflow event — how an event reaches a run
- subscribe — a state's wait for an event, and its promises
- Transitions — where this item may go next — the moves a run can make from where it is
- enter and exit (a state's arrival and departure hooks) — what runs on entering and leaving a state