# `Statifier.Machine.Content.Foreach`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.0.0/lib/statifier/machine/content/foreach.ex#L1)

A compiled `<foreach>` executable-content node (spec 4.6) - the interned
counterpart to `Statifier.Document.Foreach`. `array` is the compiled
`Machine.expr()` the iteration source evaluates once, before any
iteration (spec 4.6.3's shallow copy - see step 3 in `execute/2`'s doc
below for why one evaluation is enough). `item`/`index` are the raw
attribute strings - bare variable names by 4.6.2, so neither is compiled
or resolved any earlier than `execute/2`. `content` is the dense,
document-order `c_index` list of `<foreach>`'s own body, resolved through
`Statifier.Machine.content/2` at runtime rather than carried inline
(`Statifier.Compiler`'s Decision 2).

4.6.3's three MUST sentences this node implements (the platform's own
execution-failure notification - ADR-0003 - is what "place the error ...
in the internal event queue" below becomes, one layer up from this file;
see the "no dispatcher anywhere else" note at the end of this moduledoc
for why that conversion never happens here):

> If the SCXML processor does not support the type of the array or if the
> evaluation of the array expression fails, the SCXML processor MUST
> terminate execution of the `<foreach>` element ... and place the error
> in the internal event queue.
>
> Modifications to the collection during the execution of `<foreach>`
> MUST NOT affect the iteration behavior. ... If a shallow copy of the
> collection is possible, the iteration is done on the copy. Otherwise
> the iteration is done in such a way that iteration behavior is
> consistent with the use of a shallow copy.
>
> If the SCXML processor encounters an error while evaluating [the
> `<foreach>` element's `item` binding], it MUST cease execution of the
> `<foreach>` element **and the block that contains it**, and it MUST
> place the error in the internal event queue.

## Why this node's `execute/2` does not recurse through the block runner,
and does not write through `<assign>`'s path machinery

See `Statifier.Machine.Content.If`'s moduledoc for the four reasons a
composite node's `execute/2` folds its own children directly rather than
calling back into `Statifier.Interpreter.Content`'s block runner - they
apply here unchanged, and reason 1 is *more* binding for `<foreach>`,
since a write to `item`/`index` (or a body `<assign>`) in iteration `n`
MUST be visible in iteration `n + 1` within the same `<foreach>`.

`item`/`index` are declared and written directly against the raw
`machine_state.datamodel` with `Map.put_new/3` / `Map.put/3` - never
through `Predicator.ContextLocation.put/3`, the machinery
`Statifier.Machine.Content.Assign` uses. `<assign>`'s `check_root/3` would
*refuse* the very write 4.6.3 requires (an undeclared `item`/`index`
becoming declared is the whole point of the corpus's `test150`/`test151`),
and `item`/`index` are always bare names by Decision 1 below, so there is
no path to resolve in the first place.

## Decision 5: the shallow copy is free

`array` is evaluated exactly once, before the first iteration
(`evaluate_array/1` below), and the resulting value becomes the iteration
source for the whole loop. In an immutable language this already *is*
the shallow copy 4.6.3 asks for: an Elixir list is a value, so simply not
re-evaluating `array` per iteration satisfies "modifications to the
collection during the execution of `<foreach>` MUST NOT affect the
iteration behavior" with no copy step of its own. Do not move this
evaluation into the loop, and do not add a defensive `Enum.to_list/1` or
similar around it - both would be redundant work solving a problem this
language does not have.

## Two-element versus three-element error forms

Steps 1-4 of `execute/2` (item legality, index legality, array
evaluation, iterability) run *before* anything is declared or written, so
a failure there returns the plain two-element `{:error, reason}` form -
there is nothing yet to preserve. Step 6 (the loop itself) has, by the
time any iteration's body can fail, already declared `item`/`index` and
possibly written earlier iterations' mutations into
`machine_state.datamodel`; 4.6.3 requires those to survive a later
iteration's failure (the corpus's `test156`), so a body failure returns
the three-element `{:error, context, reason}` form instead, carrying the
context as it stood at the moment of failure. This is the single subtlest
line in this file - do not collapse the two forms into one.

Its `Statifier.ExecutableContent` implementation lives right below the
struct: this file is the whole node, top to bottom, with no dispatcher
anywhere else in the tree.

# `t`

```elixir
@type t() :: %Statifier.Machine.Content.Foreach{
  array: Statifier.Machine.expr(),
  array_location: Statifier.Parser.Location.t() | nil,
  c_index: non_neg_integer(),
  content: [non_neg_integer()],
  index: String.t() | nil,
  index_location: Statifier.Parser.Location.t() | nil,
  item: String.t(),
  item_location: Statifier.Parser.Location.t() | nil,
  location: Statifier.Parser.Location.t()
}
```

---

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