# `Schooner.Library`
[🔗](https://github.com/ausimian/schooner/blob/1.0.0/lib/schooner/library.ex#L1)

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.

# `export`

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

# `name`

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

# `registry`

```elixir
@type registry() :: %{required(name()) =&gt; t()}
```

# `segment`

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

# `source`

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

# `t`

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

# `canonicalise_name`

```elixir
@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!`

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

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

# `lookup`

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

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

# `new`

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

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

# `persist!`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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]`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
