# `ExDatalog.Callback`
[🔗](https://github.com/thanos/ex_datalog/blob/v0.5.0/lib/ex_datalog/callback.ex#L1)

A BEAM callback predicate: an Elixir function invoked during rule evaluation.

Callbacks let rule bodies call deterministic, side-effect-free Elixir
functions as predicates. A callback appears in a rule body as
`{:callback, %ExDatalog.Callback{}}`, alongside positive/negative atoms.

## Fields

- `module` — the module exporting the function.
- `function` — the function name (atom).
- `args` — a list of `ExDatalog.Term.t()` (variables/constants) resolved
  against the binding and passed positionally to the function.
- `result` — `nil` for boolean (filter) callbacks; `{:var, name}` for
  value-returning callbacks that bind the function's return value.

## Safety contract

A callback **must** be:

- **Deterministic** — the same arguments always produce the same result.
- **Side-effect free** — no I/O, mutation, or messaging.

The engine enforces only timeout and exception isolation: a callback that
exceeds `callback_timeout_ms` (default 100ms) or raises is treated as a
filtered binding (the fact is not derived). Determinism and purity are
caller contracts, not enforced by the engine.

## Examples

    iex> alias ExDatalog.{Callback, Term}
    iex> Callback.new(String, :starts_with?, [Term.var("S"), Term.const("a")])
    %ExDatalog.Callback{module: String, function: :starts_with?, args: [{:var, "S"}, {:const, "a"}], result: nil}

# `t`

```elixir
@type t() :: %ExDatalog.Callback{
  args: [ExDatalog.Term.t()],
  function: atom(),
  module: module(),
  result: ExDatalog.Term.t() | nil
}
```

# `input_variables`

```elixir
@spec input_variables(t()) :: [String.t()]
```

Returns the input variable names referenced by the callback's arguments.

These must be bound by positive body atoms before the callback runs.

## Examples

    iex> alias ExDatalog.{Callback, Term}
    iex> cb = Callback.new(MyMod, :ok?, [Term.var("A"), Term.const(1), Term.var("B")])
    iex> ExDatalog.Callback.input_variables(cb)
    ["A", "B"]

# `new`

```elixir
@spec new(module(), atom(), [ExDatalog.Term.t()], ExDatalog.Term.t() | nil) :: t()
```

Constructs a callback predicate.

`result` defaults to `nil` (a boolean filter callback). Pass a
`{:var, name}` term to bind the function's return value.

## Examples

    iex> alias ExDatalog.{Callback, Term}
    iex> Callback.new(MyMod, :adult?, [Term.var("Age")])
    %ExDatalog.Callback{module: MyMod, function: :adult?, args: [{:var, "Age"}], result: nil}

    iex> alias ExDatalog.{Callback, Term}
    iex> Callback.new(MyMod, :score, [Term.var("X")], Term.var("S"))
    %ExDatalog.Callback{module: MyMod, function: :score, args: [{:var, "X"}], result: {:var, "S"}}

# `result_variable`

```elixir
@spec result_variable(t()) :: String.t() | nil
```

Returns the result variable name for a value-returning callback, or `nil`.

## Examples

    iex> alias ExDatalog.{Callback, Term}
    iex> ExDatalog.Callback.result_variable(Callback.new(M, :f, [Term.var("X")], Term.var("R")))
    "R"

    iex> alias ExDatalog.{Callback, Term}
    iex> ExDatalog.Callback.result_variable(Callback.new(M, :f, [Term.var("X")]))
    nil

---

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