Spec 5.10's system variables, as the plain maps
Statifier.MachineState.datamodel carries them in. Two functions, so that
neither Statifier.MachineState nor Statifier.Interpreter grows spec
5.10 knowledge of its own - MachineState.new/2 calls initial/2 once,
and MachineState.put_event/2 calls event/1 on every write.
Every writer here spells "declared, no value yet" as :undefined directly,
never nil - nil is reserved for predicator's own null (ADR-0037,
docs/adr/0037-unbound-spelled-undefined-at-the-writer.md). Writing the
spec-correct "not bound" answer at the source means a
Statifier.Evaluator.context/1 wrap needs no normalization pass to produce
it.
Summary
Functions
_event's value for event - spec 5.10.1's fields, read straight off
Statifier.Event. sendid/origin/origintype/invokeid are
String.t() | nil on Statifier.Event (a datamodel null can never be one
of them, so nil there is unambiguous - Statifier.Event's moduledoc
makes the same argument); absent/1 translates a nil to :undefined
here, where they cross into the datamodel (the same translation
initial/2 reuses for _name), so _event's own fields spell "declared,
no value yet" the way every other datamodel writer does. data is not
translated - Statifier.EventData.coerce/1
already spells :undefined for "no data" and nil for a null payload, so
it passes through verbatim.
All four system variables (spec 5.10) as they stand before any event.
Called once, by MachineState.new/2.
The SCXML Event I/O Processor's type URI (spec 6.2.5 / C.1). Public so the
session's target router names the same string this module keys
_ioprocessors by, rather than a second copy of it.
The address C.1.1 asks for: a value external entities can use to reach this
session, which C.1 also makes the delivered event's origin and 5.10.1
requires to work as a <send target>. _sessionid stays the bare session id.
Functions
@spec event(event :: Statifier.Event.t()) :: map()
_event's value for event - spec 5.10.1's fields, read straight off
Statifier.Event. sendid/origin/origintype/invokeid are
String.t() | nil on Statifier.Event (a datamodel null can never be one
of them, so nil there is unambiguous - Statifier.Event's moduledoc
makes the same argument); absent/1 translates a nil to :undefined
here, where they cross into the datamodel (the same translation
initial/2 reuses for _name), so _event's own fields spell "declared,
no value yet" the way every other datamodel writer does. data is not
translated - Statifier.EventData.coerce/1
already spells :undefined for "no data" and nil for a null payload, so
it passes through verbatim.
@spec initial(machine :: Statifier.Machine.t(), session_id :: String.t()) :: map()
All four system variables (spec 5.10) as they stand before any event.
Called once, by MachineState.new/2.
_sessionid, _name, and _ioprocessors are session-lifetime and are
never rewritten afterward - _sessionid stays stable for the session's
whole lifetime (ADR-0008). _event is different: it is seeded here to
:undefined and thereafter written only by MachineState.put_event/2.
_name binds machine.name (String.t() | nil on Statifier.Machine,
since a <scxml> element may omit the optional name attribute). Spec
5.10 only says the Processor "MUST bind the variable _name ... to the
value of the 'name' attribute", and is silent on an absent attribute; an
absent optional attribute is exactly "declared, no value yet", so a nil
machine.name runs through absent/1 the same as an absent _event
field does, and _name reads as :undefined rather than as a datamodel
null.
Why _event is seeded rather than left absent
Spec 5.10's system variables are declared for the session's whole
lifetime; _event merely has no value until an event is being processed.
A datamodel is a plain map, so the only way to say "declared, no value
yet" is to bind the key to :undefined directly. Leaving the key
out instead says something different and wrong - "no such variable" -
which under Statifier.Evaluator.context/1's on_unbound: :error
(ADR-0014 item 5) makes every pre-event _event reference an
UndefinedVariableError rather than the undefined value the spec wants.
The W3C corpus is the evidence for which reading is right: test319 asserts
that _event compares equal to undefined before any event has been
processed, and takes its <else> branch to pass. Under the ECMAScript
datamodel those tests were written against, an undeclared identifier
throws ReferenceError - so the test can only pass if _event is
declared and holds undefined, which is exactly what seeding reproduces.
@spec scxml_event_processor() :: String.t()
The SCXML Event I/O Processor's type URI (spec 6.2.5 / C.1). Public so the
session's target router names the same string this module keys
_ioprocessors by, rather than a second copy of it.
The address C.1.1 asks for: a value external entities can use to reach this
session, which C.1 also makes the delivered event's origin and 5.10.1
requires to work as a <send target>. _sessionid stays the bare session id.