# `Schooner.Host`
[🔗](https://github.com/ausimian/schooner/blob/1.1.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. Going through the
named constructors and accessors keeps host code independent of the
value representation, even where a helper is currently the
identity (Schooner strings are bare Elixir binaries today).

Use `raise_error/2` or `raise_value/1` to raise failures that a
script can catch with `guard` or `with-exception-handler`.

## 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

`Schooner.Host.to_string/1` collides with `Kernel.to_string/1`.
**Don't `import Schooner.Host`.** Use `alias Schooner.Host` and
call the accessors as `Host.to_string!` etc.

## 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()
```

# `raise_error`

```elixir
@spec raise_error(binary(), [Schooner.Value.t()]) :: no_return()
```

Raise a Scheme error object of kind `:user` with `message` and `irritants`.

A script's `guard` or `with-exception-handler` catches the error.
With no handler, it surfaces to the embedder as `Schooner.Error`:
`Schooner.eval/2` returns `{:error, %Schooner.Error{}}`.
Raising `Schooner.Error` directly with `Kernel.raise/2` bypasses
Scheme handlers.

`message` must be an Elixir string and `irritants` a list of Scheme
values. A non-binary message or non-list irritants raises
`ArgumentError`, indicating a host programming error.

```elixir
alias Schooner.Host

Host.raise_error("db query failed", [Host.foreign(:timeout)])
```

# `raise_value`

```elixir
@spec raise_value(Schooner.Value.t()) :: no_return()
```

Raise any Scheme `value`, including a symbol or a host-built error object.

A script's `guard` or `with-exception-handler` catches the value.
With no handler, it surfaces to the embedder as `Schooner.Error`:
`Schooner.eval/2` returns `{:error, %Schooner.Error{}}`.
Raising `Schooner.Error` directly with `Kernel.raise/2` bypasses
Scheme handlers.

```elixir
alias Schooner.Host

Host.raise_value(Host.symbol("service-unavailable"))
```

# `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`. This is a type check, not a truthiness test: every
value other than `#f` is truthy in Scheme, but only `#t` and `#f`
are booleans. Use `Schooner.Value.truthy?/1` for truthiness.

# `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, which suits `Enum`-based host code. Use
`List.to_tuple/1` on the result if a tuple is needed.

# `vector`

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

---

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