Statifier.Evaluator.Functions (Statifier v2.15.0)

Copy Markdown View Source

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).

Summary

Types

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.

Functions

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.

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.

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.

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.

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

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

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

Types

registered()

@type registered() :: %{
  required(String.t()) =>
    {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.

Functions

base_context()

@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()

@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(providers)

@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!(providers)

@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()

@spec functions() :: %{required(String.t()) => {non_neg_integer(), atom()}}

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

in_state(list, context)

@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(providers)

@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, []}.