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

Helpers for authoring Elixir-side host functions exposed to Scheme.

Schooner does not auto-marshal across the host boundary. A host
function has the same shape as a built-in primitive — it consumes a
list of `t:Schooner.Value.t/0` arguments and returns a
`t:Schooner.Value.t/0` — and uses the helpers in this module to move
between Scheme values and idiomatic Elixir terms. The named
constructors and accessors form the seam that future
representation changes pivot on; even when the underlying impl is
the identity (Schooner strings are bare Elixir binaries today),
host code stays representation-agnostic by going through the
helpers.

## Naming convention

Asserting accessors (`to_*!/2`) raise `Schooner.Host.TypeError` when
the input does not match the expected shape. They take a keyword
argument with `:op` set to a host-supplied label for the call site
so the error points at the wrapper, not the bare predicate.

Total accessors (`to_*/1`) return `{:ok, term()} | :error` for the
"branch on shape" case where a type mismatch is not an error.

## Recommended use pattern

Several accessor names — `Schooner.Host.to_string/1` and
`Schooner.Host.to_string!/2` — collide with `Kernel.to_string/1`.
**Don't `import Schooner.Host`.** Use `alias Schooner.Host` and
call the accessors as `Host.to_string!` etc.; the alias is one
line and the call sites stay explicit.

## Worked example

    defmodule MyApp.SchemeLib do
      alias Schooner.Host

      @spec specs() :: [{binary(), Schooner.Value.arity_spec(), fun()}]
      def specs do
        [
          {"info", 1, &info/1},
          {"now-ms", 0, &now_ms/1}
        ]
      end

      defp info([msg]) do
        text = Host.to_string!(msg, op: "myapp/info")
        IO.puts(text)
        :unspecified
      end

      defp now_ms([]) do
        System.system_time(:millisecond)
      end
    end

# `bool`

```elixir
@spec bool(boolean()) :: Schooner.Value.bool_v()
```

# `bytevector`

```elixir
@spec bytevector([byte()] | binary()) :: Schooner.Value.bytevector_v()
```

# `char`

```elixir
@spec char(non_neg_integer()) :: Schooner.Value.char_v()
```

# `foreign`

```elixir
@spec foreign(term()) :: Schooner.Value.foreign_v()
```

# `library`

```elixir
@spec library(keyword()) :: Schooner.Library.t()
```

Build a `Schooner.Library` from host-supplied primitive specs and
arbitrary value bindings.

## Options

  * `:name` — canonical library name as a list of binary or
    non-negative-integer segments. Defaults to `[]`, which marks
    the library as **anonymous**: it cannot be reached via Scheme
    `(import ...)`, and its bindings are applied directly to the
    runtime environment when the embedder lists it. Named
    libraries (e.g. `["myapp", "log"]`) are sandbox-safe — the
    script must explicitly `(import (myapp log))` to see them.

  * `:primitives` — list of `{name, arity, fun}` triples
    registered as `Schooner.Value.primitive/3` bindings. `arity`
    may be an integer, `{:at_least, n}`, or `{:between, lo, hi}`.
    `fun` must be `(list() -> Schooner.Value.t())` — the
    underlying primitive ABI.

  * `:values` — list of `{name, value}` pairs for arbitrary
    `t:Schooner.Value.t/0` bindings (constants, foreign-wrapped
    service handles, pre-built closures). Useful when the host
    wants to expose data, not just procedures.

Names within a library must be unique across `:primitives` and
`:values` combined; a collision raises `ArgumentError`.

## Examples

Named library — script must import:

    Schooner.Host.library(
      name: ["myapp", "log"],
      primitives: [
        {"info", 1, &MyApp.SchemeLib.info/1}
      ]
    )

Anonymous library — bindings auto-applied, no import required.
Sandbox-loosening; list deliberately:

    Schooner.Host.library(
      primitives: [
        {"now-ms", 0, fn [] -> System.system_time(:millisecond) end}
      ]
    )

# `list`

```elixir
@spec list([Schooner.Value.t()]) :: Schooner.Value.t()
```

# `pair`

```elixir
@spec pair(Schooner.Value.t(), Schooner.Value.t()) :: Schooner.Value.pair_v()
```

# `primitive`

```elixir
@spec primitive(binary(), Schooner.Value.arity_spec(), (list() -&gt; Schooner.Value.t())) ::
  Schooner.Value.primitive_v()
```

# `string`

```elixir
@spec string(binary()) :: Schooner.Value.string_v()
```

# `symbol`

```elixir
@spec symbol(binary()) :: Schooner.Value.sym_v()
```

# `to_bool`

```elixir
@spec to_bool(Schooner.Value.t()) :: {:ok, boolean()} | :error
```

# `to_bool!`

```elixir
@spec to_bool!(
  Schooner.Value.t(),
  keyword()
) :: boolean()
```

Assert `value` is a Scheme boolean and return the Elixir
`true | false`. Note: this differs from "truthy" — only `false`
is Scheme-falsy, but every other value is *not* a Scheme boolean.
Use `Schooner.Value.truthy?/1` for the truthiness test.

# `to_bytevector`

```elixir
@spec to_bytevector(Schooner.Value.t()) :: {:ok, binary()} | :error
```

# `to_bytevector!`

```elixir
@spec to_bytevector!(
  Schooner.Value.t(),
  keyword()
) :: binary()
```

Assert `value` is a Scheme bytevector and return the underlying
Elixir binary.

# `to_char`

```elixir
@spec to_char(Schooner.Value.t()) :: {:ok, non_neg_integer()} | :error
```

# `to_char!`

```elixir
@spec to_char!(
  Schooner.Value.t(),
  keyword()
) :: non_neg_integer()
```

Assert `value` is a Scheme character and return the codepoint
integer.

# `to_float`

```elixir
@spec to_float(Schooner.Value.t()) :: {:ok, float()} | :error
```

# `to_float!`

```elixir
@spec to_float!(
  Schooner.Value.t(),
  keyword()
) :: float()
```

Assert `value` is a bare inexact real and return the Elixir float.
Rejects integers, rationals, the float specials (`+inf.0`,
`-inf.0`, `+nan.0`), and complex. Hosts that want "any real
number, coerced" should call `to_real!/2` instead.

# `to_foreign_ref`

```elixir
@spec to_foreign_ref(Schooner.Value.t()) :: {:ok, term()} | :error
```

# `to_foreign_ref!`

```elixir
@spec to_foreign_ref!(
  Schooner.Value.t(),
  keyword()
) :: term()
```

Assert `value` is a foreign-wrapped host term and return the
wrapped term. Mirrors `Schooner.Value.foreign_ref/1` but raises
the host-facing `TypeError` instead of `ArgumentError`.

# `to_integer`

```elixir
@spec to_integer(Schooner.Value.t()) :: {:ok, integer()} | :error
```

# `to_integer!`

```elixir
@spec to_integer!(
  Schooner.Value.t(),
  keyword()
) :: integer()
```

Assert `value` is an exact integer and return the bare Elixir
integer. Rejects rationals, floats, the float specials, and
complex.

# `to_list`

```elixir
@spec to_list(Schooner.Value.t()) :: {:ok, [Schooner.Value.t()]} | :error
```

# `to_list!`

```elixir
@spec to_list!(
  Schooner.Value.t(),
  keyword()
) :: [Schooner.Value.t()]
```

Assert `value` is a proper Scheme list and return the underlying
Elixir list. Raises on improper lists and non-list values.

# `to_proc`

```elixir
@spec to_proc(Schooner.Value.t()) :: {:ok, Schooner.Value.t()} | :error
```

# `to_proc!`

```elixir
@spec to_proc!(
  Schooner.Value.t(),
  keyword()
) :: Schooner.Value.t()
```

Assert `value` is callable from Elixir — a closure, a primitive,
or a parameter — and return it unchanged. Use this to validate
the shape of a callback argument before passing it to
`Schooner.apply/2`.

# `to_real`

```elixir
@spec to_real(Schooner.Value.t()) :: {:ok, number()} | :error
```

# `to_real!`

```elixir
@spec to_real!(
  Schooner.Value.t(),
  keyword()
) :: number()
```

Assert `value` is any real number and return it as an Elixir
number. Integers and floats pass through; rationals are coerced
to their float approximation; the float specials are rejected
(no plain BEAM-float representation).

# `to_string`

```elixir
@spec to_string(Schooner.Value.t()) :: {:ok, binary()} | :error
```

# `to_string!`

```elixir
@spec to_string!(
  Schooner.Value.t(),
  keyword()
) :: binary()
```

Assert `value` is a Scheme string and return the underlying Elixir
binary. Rejects symbols, bytevectors, and any non-string.

# `to_symbol_name`

```elixir
@spec to_symbol_name(Schooner.Value.t()) :: {:ok, binary()} | :error
```

# `to_symbol_name!`

```elixir
@spec to_symbol_name!(
  Schooner.Value.t(),
  keyword()
) :: binary()
```

Assert `value` is a Scheme symbol and return its name as an Elixir
binary.

# `to_vector`

```elixir
@spec to_vector(Schooner.Value.t()) :: {:ok, [Schooner.Value.t()]} | :error
```

# `to_vector!`

```elixir
@spec to_vector!(
  Schooner.Value.t(),
  keyword()
) :: [Schooner.Value.t()]
```

Assert `value` is a Scheme vector and return its elements as an
Elixir list. The list shape is more idiomatic for Elixir
iteration; hosts that want a tuple can call `Tuple.to_list/1`'s
inverse, but most use cases want `Enum`.

# `vector`

```elixir
@spec vector([Schooner.Value.t()]) :: Schooner.Value.vector_v()
```

---

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