Schooner.Library (schooner v1.0.0)

Copy Markdown View Source

Registry entries for r7rs libraries.

A %Schooner.Library{} is the result of compiling a library — a frozen exports map plus the dependency list and provenance metadata the importer and the diagnostics need. It is not a representation of the library's source: the original datums and the expanded body AST are dropped after the body has run and the exports have been materialised. Closures that escape via :exports carry their own body AST inside {:closure, params, body, env, name}; macros survive as transformers.

Names are canonical lists of segments where each segment is either a binary (Scheme-symbol form) or a non-negative integer. The reader will produce datum-form names like [{:sym, "scheme"}, {:sym, "base"}] once import and define-library land; this module accepts only the canonicalised form so the registry key is ==-comparable.

Registries and persistent storage

A registry is a plain map of name => %Schooner.Library{}. register/2 and lookup/2 are pure and operate on a map the caller owns. The standard library registry is built once at OTP application start and stashed under the persistent term key {Schooner.Library, :standard}. standard/0 reads it without copying, so spawned evaluator processes pay no cost to access it.

Summary

Functions

Convert a Scheme datum form of a library name (e.g. (scheme base) read as a cons-cell list of {:sym, _} segments) into the canonical registry-key form (["scheme", "base"]). Symbol segments become binaries; non-negative integer segments pass through.

Look up name or raise Schooner.Library.NotFoundError if absent.

Look up name in registry. Returns {:ok, library} or :error.

Build a library entry. :name is required; other fields default empty (exports: %{}, imports: [], source: :native, features: []).

Persist registry as the standard registry. Triggers a global literal-area GC if the key is being updated rather than created; callers expect to use this only at OTP application start.

Insert library into registry. Raises ArgumentError if a library with the same canonical name is already present — registries are append-only.

Render name in (scheme base) form for diagnostics. The empty list (an anonymous host library) renders as "(anonymous host library)" — there is no Scheme syntax that can produce that name, so a literal () rendering would be misleading.

Read the standard registry from persistent term storage. Returns %{} when the key is unset, which lets the standard-library bootstrapper build the first registry on top of an empty default.

Topologically sort registry by :imports. Returns the names in an order where every import precedes the library that imports it. Edges pointing at libraries not present in registry are silently skipped — import resolution surfaces those at use site.

Types

export()

@type export() :: {:var, term()} | {:macro, term()}

name()

@type name() :: [segment()]

registry()

@type registry() :: %{required(name()) => t()}

segment()

@type segment() :: binary() | non_neg_integer()

source()

@type source() :: :native | {:file, binary(), term()}

t()

@type t() :: %Schooner.Library{
  exports: %{required(binary()) => export()},
  features: [atom()],
  imports: [name()],
  name: name(),
  source: source()
}

Functions

canonicalise_name(datum)

@spec canonicalise_name(Schooner.Value.t()) :: name()

Convert a Scheme datum form of a library name (e.g. (scheme base) read as a cons-cell list of {:sym, _} segments) into the canonical registry-key form (["scheme", "base"]). Symbol segments become binaries; non-negative integer segments pass through.

fetch!(registry, name)

@spec fetch!(registry(), name()) :: t()

Look up name or raise Schooner.Library.NotFoundError if absent.

lookup(registry, name)

@spec lookup(registry(), name()) :: {:ok, t()} | :error

Look up name in registry. Returns {:ok, library} or :error.

new(opts)

@spec new(keyword()) :: t()

Build a library entry. :name is required; other fields default empty (exports: %{}, imports: [], source: :native, features: []).

persist!(registry)

@spec persist!(registry()) :: :ok

Persist registry as the standard registry. Triggers a global literal-area GC if the key is being updated rather than created; callers expect to use this only at OTP application start.

register(registry, library)

@spec register(registry(), t()) :: registry()

Insert library into registry. Raises ArgumentError if a library with the same canonical name is already present — registries are append-only.

render_name(name)

@spec render_name(name()) :: binary()

Render name in (scheme base) form for diagnostics. The empty list (an anonymous host library) renders as "(anonymous host library)" — there is no Scheme syntax that can produce that name, so a literal () rendering would be misleading.

standard()

@spec standard() :: registry()

Read the standard registry from persistent term storage. Returns %{} when the key is unset, which lets the standard-library bootstrapper build the first registry on top of an empty default.

topo_sort(registry)

@spec topo_sort(registry()) :: {:ok, [name()]} | {:error, {:cycle, [name()]}}

Topologically sort registry by :imports. Returns the names in an order where every import precedes the library that imports it. Edges pointing at libraries not present in registry are silently skipped — import resolution surfaces those at use site.

Returns {:error, {:cycle, names}} when registry contains an import cycle. names lists the cycle entry-to-cycle-back order so (a) imports (b), (b) imports (a) reports [a, b, a].