Logos.Namespace (Logos v0.2.0)

Copy Markdown

Functions operating against a Logos.Runtime's ETS table -- there is no free-floating %Logos.Namespace{} struct someone could get out of sync with the registry; every operation here reads/writes runtime's table directly. See Logos.Runtime's moduledoc for the exact row shapes.

A namespace row is %{meta:, aliases:, refers:, auto_refer_core?:, var_names:}:

  • aliases -- %{alias_name => target_ns_name}, populated by alias!/4 (the Logos.Var-analogue of :require ... :as).
  • refers -- %{name => {target_ns_name, target_name}}, populated by refer!/4 (:refer/use).
  • var_names -- the MapSet of names interned directly in this namespace (via intern!/5), kept for refer!/4's "every public var this namespace defines" enumeration and for qualified-symbol existence checks.
  • auto_refer_core? -- every namespace except logos.core itself implicitly sees logos.core's public vars, so the standard library (interned into logos.core by both the Layer-1 primitives and priv/stdlib/*.logos's Layer-2 macros/functions) is visible everywhere without an explicit require. Implemented as a resolution-time fallback (Logos.Eval.resolve_symbol_location/2 checks logos.core directly when this flag is set and nothing else matched) rather than as a literal one-time copy into refers: a snapshot-copy taken at ensure!/2 time would go stale the moment logos.core gains a new var afterward (exactly what happens during bootstrap: user is ensure!'d before the priv/stdlib/*.logos files finish loading if/let/etc. into logos.core). Resolving dynamically instead means user (and every other namespace) always sees the current contents of logos.core, which is both simpler to get right during bootstrap and arguably closer to what "implicit refer" should mean.

Summary

Functions

Registers alias_name -> target_ns_name in ns_name's alias table.

Every namespace name currently registered in runtime.

Replaces ns_name/var_name's metadata with fun.(old_meta).

Whether ns_name implicitly refers logos.core (true for every namespace except logos.core itself).

Creates namespace name if it doesn't already exist. Idempotent. logos.core itself never auto-refers itself.

Whether namespace name exists in runtime.

Looks up name in ns_name's explicit refers table only (not the implicit logos.core fallback -- see Logos.Eval.resolve_symbol_location/2 for the full chain).

The raw %{value:, meta:} row for ns_name/var_name, or :error if no such Var.

The current value of ns_name/var_name, or :error if no such Var.

Whether ns_name interns a var literally named var_name (not via refer).

Interns (creates or overwrites) var_name in ns_name with value, merging meta into the Var's metadata. Creates ns_name first if absent.

Whether ns_name/var_name is public, i.e. its metadata's :private key is not truthy. A nonexistent Var is not public.

Pulls public vars from from_ns into ns_name's refers table (:only/:exclude supported; :rename is not implemented). Private vars (:private truthy in metadata, checked via public?/3) are never referred, regardless of :only/:exclude -- require/use only ever refer public vars.

Resolves alias_or_ns against ns_name's aliases, falling back to treating it as a literal namespace name if no alias matches. Used when resolving a qualified symbol like ns/name.

The (meta, aliases, refers, var_names, auto_refer_core?) row for name, or :error if it doesn't exist.

Mutates ns_name/var_name's value in place (the Logos.Var "mutable box" -- see that module).

Types

row()

@type row() :: %{
  meta: map(),
  aliases: %{required(String.t()) => String.t()},
  refers: %{required(String.t()) => {String.t(), String.t()}},
  auto_refer_core?: boolean(),
  var_names: MapSet.t(String.t())
}

Functions

alias!(runtime, ns_name, alias_name, target_ns_name)

@spec alias!(Logos.Runtime.t(), String.t(), String.t(), String.t()) :: :ok

Registers alias_name -> target_ns_name in ns_name's alias table.

all(runtime)

@spec all(Logos.Runtime.t()) :: [String.t()]

Every namespace name currently registered in runtime.

alter_meta!(runtime, ns_name, var_name, fun)

@spec alter_meta!(Logos.Runtime.t(), String.t(), String.t(), (map() -> map())) ::
  :ok | {:error, {:no_such_var, String.t(), String.t()}}

Replaces ns_name/var_name's metadata with fun.(old_meta).

auto_refer_core?(runtime, ns_name)

@spec auto_refer_core?(Logos.Runtime.t(), String.t()) :: boolean()

Whether ns_name implicitly refers logos.core (true for every namespace except logos.core itself).

ensure!(runtime, name)

@spec ensure!(Logos.Runtime.t(), String.t()) :: :ok

Creates namespace name if it doesn't already exist. Idempotent. logos.core itself never auto-refers itself.

exists?(runtime, name)

@spec exists?(Logos.Runtime.t(), String.t()) :: boolean()

Whether namespace name exists in runtime.

get_refer(runtime, ns_name, name)

@spec get_refer(Logos.Runtime.t(), String.t(), String.t()) ::
  {:ok, {String.t(), String.t()}} | :error

Looks up name in ns_name's explicit refers table only (not the implicit logos.core fallback -- see Logos.Eval.resolve_symbol_location/2 for the full chain).

get_var_row(runtime, ns_name, var_name)

@spec get_var_row(Logos.Runtime.t(), String.t(), String.t()) ::
  {:ok, %{value: term(), meta: map()}} | :error

The raw %{value:, meta:} row for ns_name/var_name, or :error if no such Var.

get_var_value(runtime, ns_name, var_name)

@spec get_var_value(Logos.Runtime.t(), String.t(), String.t()) ::
  {:ok, term()} | :error

The current value of ns_name/var_name, or :error if no such Var.

has_var?(runtime, ns_name, var_name)

@spec has_var?(Logos.Runtime.t(), String.t(), String.t()) :: boolean()

Whether ns_name interns a var literally named var_name (not via refer).

intern!(runtime, ns_name, var_name, value, meta \\ %{})

@spec intern!(Logos.Runtime.t(), String.t(), String.t(), term(), map()) :: :ok

Interns (creates or overwrites) var_name in ns_name with value, merging meta into the Var's metadata. Creates ns_name first if absent.

public?(runtime, ns_name, var_name)

@spec public?(Logos.Runtime.t(), String.t(), String.t()) :: boolean()

Whether ns_name/var_name is public, i.e. its metadata's :private key is not truthy. A nonexistent Var is not public.

refer!(runtime, ns_name, from_ns, opts \\ [])

@spec refer!(Logos.Runtime.t(), String.t(), String.t(), keyword()) ::
  :ok | {:error, {:no_such_namespace, String.t()}}

Pulls public vars from from_ns into ns_name's refers table (:only/:exclude supported; :rename is not implemented). Private vars (:private truthy in metadata, checked via public?/3) are never referred, regardless of :only/:exclude -- require/use only ever refer public vars.

opts:

  • :only -- a list of String.t() names; when given, only those names (still filtered through public?/3) are referred.
  • :exclude -- a list of String.t() names to skip.

resolve_alias(runtime, ns_name, alias_or_ns)

@spec resolve_alias(Logos.Runtime.t(), String.t(), String.t()) :: String.t()

Resolves alias_or_ns against ns_name's aliases, falling back to treating it as a literal namespace name if no alias matches. Used when resolving a qualified symbol like ns/name.

row(runtime, name)

@spec row(Logos.Runtime.t(), String.t()) :: {:ok, row()} | :error

The (meta, aliases, refers, var_names, auto_refer_core?) row for name, or :error if it doesn't exist.

set_var_value!(runtime, ns_name, var_name, value)

@spec set_var_value!(Logos.Runtime.t(), String.t(), String.t(), term()) ::
  :ok | {:error, {:no_such_var, String.t(), String.t()}}

Mutates ns_name/var_name's value in place (the Logos.Var "mutable box" -- see that module).