ExTLA.State (ExTLA v0.6.0)

Copy Markdown View Source

A state is an immutable snapshot of all specification variables. A step is a pair of consecutive states (pre-state and post-state). A behavior is a (possibly infinite) sequence of states.

A state is a plain map, deliberately: it is the value the DSL's transitions build and the value ExTLA.Fingerprint hashes, and wrapping it in a struct would buy nothing but an extra field in every fingerprint.

Missing variables raise

get/2, unchanged?/2 and prime/3 all fail loudly on a variable the state does not have. This is not defensiveness; it is the difference between a bug and a wrong answer. A misspelled variable read through Map.get/2 returns nil, nil compares unequal to whatever the guard expected, the action looks disabled, and the checker explores a smaller state space and reports success. Nothing in the output distinguishes that run from a sound one — the same failure mode, a success reported over states never visited, that ExTLA.Fingerprint prevents at the level below.

Summary

Functions

Return the set of variables that changed between states.

Return the value of a variable in the given state.

Create a new state from a keyword list or map of variable assignments.

Return a new state with the given variable updated. Analogous to TLA+ primed variable assignment: x' = value.

Return a new state with multiple variables updated.

Check if two states are identical (stuttering step).

Assert that specified variables did NOT change between states. Analogous to TLA+ UNCHANGED <<x, y, z>>.

Assert that every value in the state is deterministically serializable, per 02-dsl-semantics.md §6.

Types

behavior()

@type behavior() :: [t(), ...]

step()

@type step() :: {pre :: t(), post :: t()}

t()

@type t() :: %{required(variable_name()) => value()}

value()

@type value() :: term()

variable_name()

@type variable_name() :: atom()

Functions

changed_vars(arg)

Return the set of variables that changed between states.

iex> ExTLA.State.changed_vars({%{x: 0, y: 1}, %{x: 2, y: 1}})
MapSet.new([:x])

get(state, var)

Return the value of a variable in the given state.

Raises KeyError if the variable is not in the state — see the note on missing variables in the module documentation.

iex> ExTLA.State.get(%{x: 0}, :x)
0

new(assignments)

Create a new state from a keyword list or map of variable assignments.

iex> ExTLA.State.new(x: 0, y: 1)
%{x: 0, y: 1}

prime(state, var, value)

Return a new state with the given variable updated. Analogous to TLA+ primed variable assignment: x' = value.

Raises KeyError if the variable is not already in the state. A TLA+ spec's variable set is fixed by its Init, so priming a variable that is not in the state is a typo, and a typo that silently widens the state is one the fingerprint would then faithfully preserve.

iex> ExTLA.State.prime(%{x: 0, y: 1}, :x, 2)
%{x: 2, y: 1}

prime_many(state, assignments)

Return a new state with multiple variables updated.

iex> ExTLA.State.prime_many(%{x: 0, y: 1}, %{x: 2, y: 3})
%{x: 2, y: 3}

stuttering?(arg)

Check if two states are identical (stuttering step).

iex> ExTLA.State.stuttering?({%{x: 0}, %{x: 0}})
true

unchanged?(arg, vars)

Assert that specified variables did NOT change between states. Analogous to TLA+ UNCHANGED <<x, y, z>>.

iex> ExTLA.State.unchanged?({%{x: 0, y: 1}, %{x: 2, y: 1}}, [:y])
true

validate!(state)

Assert that every value in the state is deterministically serializable, per 02-dsl-semantics.md §6.

Returns the state, so it can be threaded into a pipeline. Raises ExTLA.State.InvalidTermError naming the variable and the position inside it.

Pids, references and ports have no stable identity across runs, and funs serialize their defining module's version. Floats are excluded for a subtler reason: 0.0 and -0.0 compare equal under == but encode differently, so two states that a spec author would call equal would be explored twice — and :nan never equals itself, so a state containing one is not even equal to itself.

iex> ExTLA.State.validate!(%{x: 0, q: [:a, {1, "b"}]})
%{x: 0, q: [:a, {1, "b"}]}