# `Statifier.Evaluator.SystemVariables`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.0.0/lib/statifier/evaluator/system_variables.ex#L1)

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.

# `event`

```elixir
@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.

# `initial`

```elixir
@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.

# `scxml_event_processor`

```elixir
@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.

# `scxml_location`

```elixir
@spec scxml_location(session_id :: String.t()) :: String.t()
```

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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
