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

The `Predicator.FunctionProvider` carrying `In/1` (spec 5.9.1), replacing
`Statifier.Evaluator`'s former private closure-building helper.

A provider entry is named by `{module, atom}` rather than by a captured
`function()` value (`deps/predicator/lib/predicator/functions/provider.ex`),
so `in_state/2` reads the current configuration from its
`%Predicator.Context{}` argument's `host` slot instead of closing over it.
`Statifier.Evaluator.context/1` sets `host` to `{machine, configuration}`
and refreshes it per site with `Predicator.Context.put_host/2` - see that
module's moduledoc for why this dissolves the "not a resumable position"
ground the closure used to force.

It also resolves a host's `:function_providers` list
(`from_providers/1`) into the full dispatch map a session stamps onto
`Statifier.MachineState`'s `functions` field, which `Statifier.Evaluator.context/1` then
uses in place of the constant's own map (ADR-0078).

# `registered`

```elixir
@type registered() :: %{
  required(String.t()) =&gt;
    {Predicator.Evaluator.function_arity(), {module(), atom()}}
}
```

A resolved dispatch map: function name to `{arity, {module, atom}}`, the
shape `Predicator.Context` keeps in its `functions` field. The map
`from_providers/1` returns holds every name `base_context/0` holds plus
each host provider's names.

# `base_context`

```elixir
@spec base_context() :: Predicator.Context.t()
```

The fixed half of every context this library builds: an empty `data`, a
`nil` `host`, `on_unbound: :error`, and `functions` resolved once at
compile time to `%{"In" => {1, {Statifier.Evaluator.Functions, :in_state}}}` plus
the four builtin providers. `Statifier.Evaluator.context/1` starts from
this constant on every call, then layers in `host` via
`Predicator.Context.put_host/2` and the datamodel via
`Statifier.Evaluator.bind/3` - so `Predicator.Context.new/2` and
`Predicator.Context.resolve_functions/1` never run on that hot path.

Two calls return the same term; nothing about this value varies at
runtime.

# `base_names`

```elixir
@spec base_names() :: [String.t()]
```

The names every chart can call without registering anything: `In` and
the builtin providers' names, sorted. `Statifier.compile/2` adds the
names of its `:function_providers` to this set before it checks the
calls a chart makes.

# `from_providers`

```elixir
@spec from_providers(providers :: [module()] | nil) ::
  {:ok, registered() | nil}
  | {:error,
     {:not_a_list, term()}
     | {:invalid_provider, term(), String.t()}
     | {:shadowed_names, [String.t()]}}
```

Resolves a host's `:function_providers` list into the dispatch map an
evaluation context uses: `base_context/0`'s map with every provider's
functions merged in.

`nil` and `[]` both answer `{:ok, nil}`, which means "no host function":
`Statifier.Evaluator.context/1` then uses `base_context/0` unchanged.

Refuses, without raising, with:

  - `{:error, {:not_a_list, value}}` - the option is not a list.
  - `{:error, {:invalid_provider, module, message}}` - a list entry is
    not an atom, or names a module that does not load, export
    `functions/0`, or export each named function at arity 2.
    `Predicator.Context.resolve_functions/1` does the check, and
    `message` is its text.
  - `{:error, {:shadowed_names, names}}` - a provider names a function
    the base set already has (`base_names/0`), so `In` and the
    builtins always mean the same thing. `names` is sorted.

# `from_providers!`

```elixir
@spec from_providers!(providers :: [module()] | nil) :: registered() | nil
```

`from_providers/1`, raising `ArgumentError` on a refusal. The form
`Statifier.MachineState.new/2` and `put_function_providers/2` use: a bad
provider list is host misuse, refused the way a bad `:datamodel` is.

# `functions`

```elixir
@spec functions() :: %{required(String.t()) =&gt; {non_neg_integer(), atom()}}
```

This provider's one entry: `In/1`, dispatched to `in_state/2`.

# `in_state`

```elixir
@spec in_state(args :: [Predicator.Types.value()], context :: Predicator.Context.t()) ::
  {:ok, boolean()}
```

`In(stateId)`: true when `stateId` names a state in `context.host`'s
configuration, false for an inactive or undeclared state id.

`context.host` must be `{machine, configuration}`, the shape
`Statifier.Evaluator.context/1` sets via `Predicator.Context.put_host/2`.

# `provider_names`

```elixir
@spec provider_names(providers :: [module()] | nil) ::
  {:ok, [String.t()]} | {:error, term()}
```

The names a `:function_providers` list registers, sorted, after the same
checks `from_providers/1` makes. `nil` and `[]` answer `{:ok, []}`.

---

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