ShotDs.Stt.Semantics (shot_ds v1.2.0)

Copy Markdown View Source

Implements the semantics of Church's simple type theory. The most important functions are subst/2 and subst!/2, which apply substitutions to a given term, and unfold_defs/2 and unfold_defs!/2, which unfold (possibly polymorphic) constant definitions while instantiating their type variables according to each occurrence's monotype.

Summary

Functions

Adds a new substitution to a list of substitutions by applying it to every member and prepending it, propagating errors.

Adds a new substitution to a list of substitutions by applying it to every member and prepending it, raising on errors.

Instantiates the bound variable with index k with the replacement term corresponding to the given id, safely propagating errors.

Instantiates the bound variable with index k with the replacement term corresponding to the given id, raising on errors. Uses caching for efficient computation.

Applies a d-shift above cutoff c to the term with the given id.

Applies a d-shift above cutoff c to the term with the given id, raising on invalid IDs.

Applies a singular substitution or a list of substitutions left to right to the term with the given id, propagating any lookup or type errors.

Applies a singular substitution or a list of substitutions left to right to the term with the given id, erroring out if it encounters an invalid ID.

Applies a type substitution (a map from type-variable references to types) to every type appearing in the term with the given id, propagating any lookup errors.

Like subst_types/2, but raises on invalid IDs.

Unfolds occurrences of all defined constants in target_id by replacing each occurrence with a type-instantiated, β-reduced copy of the corresponding definition body.

Like unfold_defs/2, but raises on invalid IDs or matching failures.

Functions

add_subst(substs, new_subst)

Adds a new substitution to a list of substitutions by applying it to every member and prepending it, propagating errors.

add_subst!(substs, new_subst)

Adds a new substitution to a list of substitutions by applying it to every member and prepending it, raising on errors.

instantiate(term_id, k, replacement_id, cache \\ %{})

@spec instantiate(
  ShotDs.Data.Term.term_id(),
  pos_integer(),
  ShotDs.Data.Term.term_id(),
  map()
) ::
  {:ok, {ShotDs.Data.Term.term_id(), map()}}
  | ShotDs.Stt.TermFactory.lookup_error_t()
  | {:error, :incompatible_types}

Instantiates the bound variable with index k with the replacement term corresponding to the given id, safely propagating errors.

instantiate!(term_id, k, replacement_id, cache \\ %{})

Instantiates the bound variable with index k with the replacement term corresponding to the given id, raising on errors. Uses caching for efficient computation.

shift(term_id, d, c \\ 0, cache \\ %{})

Applies a d-shift above cutoff c to the term with the given id.

shift!(term_id, d, c \\ 0, cache \\ %{})

Applies a d-shift above cutoff c to the term with the given id, raising on invalid IDs.

subst(substitutions, term_id)

Applies a singular substitution or a list of substitutions left to right to the term with the given id, propagating any lookup or type errors.

subst!(substitutions, term_id)

Applies a singular substitution or a list of substitutions left to right to the term with the given id, erroring out if it encounters an invalid ID.

subst_types(term_id, type_subst)

Applies a type substitution (a map from type-variable references to types) to every type appearing in the term with the given id, propagating any lookup errors.

This rewrites the head, the bound and free variables, and the term-level type, while leaving the term's tree-structure - i.e. its de Bruijn indices and abstraction nesting - untouched. Subterms whose :tvars field shares no element with the substitution's domain are skipped via short-circuit, so the cost of subst_types/2 is proportional to the part of the term that actually mentions the substituted variables.

subst_types!(term_id, type_subst)

Like subst_types/2, but raises on invalid IDs.

unfold_defs(target_id, defs)

@spec unfold_defs(ShotDs.Data.Term.term_id(), %{
  required(ShotDs.Data.Declaration.const_t()) => ShotDs.Data.Term.term_id()
}) ::
  {:ok, ShotDs.Data.Term.term_id()}
  | ShotDs.Stt.TermFactory.lookup_error_t()
  | {:error, :incompatible_types}
  | {:error, String.t()}

Unfolds occurrences of all defined constants in target_id by replacing each occurrence with a type-instantiated, β-reduced copy of the corresponding definition body.

The defs map associates polymorphic (or monomorphic) constant declarations with their definition bodies. Polymorphism is implicit: any free type variables appearing in a declaration's stored type are treated as universally quantified scheme variables, and the same variables are expected to occur in the corresponding definition body. At each occurrence of a defined constant, the declaration's polymorphic type is matched against the occurrence's instance type to derive a type substitution, which is applied to the definition body via subst_types/2 before β-reducing it with the occurrence's arguments.

Lookup is by constant name: each declaration is keyed in defs by its full struct, but only its :name is consulted at occurrences (since the type at each occurrence is the instance, not the polymorphic, type).

For a monomorphic declaration (no free type variables in its stored type) this degenerates to constant rewriting analogous to free variable substitution via subst/2.

Returns {:ok, target_id} immediately when defs is empty.

unfold_defs!(target_id, defs)

Like unfold_defs/2, but raises on invalid IDs or matching failures.