Serializable closure representation for Runic components.
A Closure combines the source AST, runtime bindings, and compile-time
metadata into a single serializable structure. This allows components
to be reconstructed from logs using term_to_binary/1 and binary_to_term/1.
Fields
:source- The original quoted AST for the closure:bindings- Map of variable names to their captured values:metadata-%Runic.ClosureMetadata{}for environment reconstruction:hash- Content-addressable hash of the closure
Examples
outer_var = 42
closure = Runic.Closure.new(
quote(do: fn x -> x + outer_var end),
%{outer_var: 42},
__ENV__
)
# Closures are serializable
binary = :erlang.term_to_binary(closure)
roundtrip = :erlang.binary_to_term(binary)
# Can be evaluated in a different context
{fun, _} = Runic.Closure.eval(closure)
fun.(10) # => 52
Summary
Functions
Evaluates a closure, returning the result and updated bindings.
Creates a new Closure from source AST, bindings, and caller environment.
Validates that all bindings in a map are serializable.
Validates that a value can be serialized with term_to_binary/1.
Types
@type t() :: %Runic.Closure{ bindings: map(), hash: integer() | nil, metadata: Runic.ClosureMetadata.t() | nil, source: Macro.t() }
Functions
Evaluates a closure, returning the result and updated bindings.
This reconstructs the evaluation environment from the closure's metadata and evaluates the source AST with the stored bindings.
Options
:base_env- Override the base environment for evaluation
Creates a new Closure from source AST, bindings, and caller environment.
Validates that all bindings are serializable before creating the closure.
Raises ArgumentError if any binding contains non-serializable values.
Validates that all bindings in a map are serializable.
Raises ArgumentError if any binding is not serializable.
Validates that a value can be serialized with term_to_binary/1.
Returns :ok if serializable, {:error, reason} otherwise.
Note: PIDs, references, and ports can technically be serialized but they are not valid across sessions/nodes, so we reject them.