apply
Cross-runtime dynamic function invocation for Erlang and JavaScript.
Resolve functions by string path in the target runtime and call them,
returning a type-checked Result(a, String) where a is the type of the
default you pass. Error messages share a unified path/arity + reason
style on both targets.
Values
pub fn apply(
raw_path: String,
args: args_tuple,
default: a,
) -> Result(a, String)
Dynamically invoke a runtime function - the main entry point.
raw_path:"Module:Function"on Erlang,"object.property"on JavaScript.args: must be a tuple; elements are spread as call arguments in order.default: pins the expected return type; the call result is type-checked against it at runtime before being returned.
Returns Result(a, String) where a is the type of default:
Ok(value)- the call succeeded and the result has the same runtime type asdefaultError(message)- bad path, target not callable, exception caught, or the result type does not matchdefault
Handling multi-type returns. Runtime functions are untyped from Gleam’s
point of view: the same path can come back as an Int on one call and a
Float or Bool on another, and “abnormal” values (undefined, false,
{error, _}, …) are just values too. default is the anchor that resolves
all of this: whatever comes back is compared against the type of default,
so you always get a type-safe Result(a, String) and never have to handle
raw any in Gleam:
apply.apply("erlang:length", #([1, 2, 3]), 0) // Ok(3) (Int)
apply.apply("Math.max", #(1, 5), 0) // Ok(5) (Int)
apply.apply("erlang:is_atom", #(1), 0)
// Error("erlang:is_atom/1 returned false (type boolean), expected type int")
The type judgement rules are shared with unwrap/2 (see there).
pub fn call_erl(raw: String, arity: Int) -> any
Quick check helper: fetch an Erlang function, crashing on failure.
Prefer apply in real code; when apply is not enough, fetch the function
first and define the call behaviour yourself.
pub fn call_js(raw: String) -> any
Quick check helper: fetch a JS object, crashing on failure.
Prefer apply in real code; when apply is not enough, fetch the object
first and define the call behaviour yourself.
pub fn get_erl_func(
raw_path: String,
erl_arity: Int,
) -> Result(any, String)
Fetch an Erlang function reference (Erlang target only).
Accepts "Module:Function", or a bare function name which is prefixed
with erlang:. Returns Ok(fun) on success; always returns a runtime
error on the JavaScript target.
let assert Ok(map) = apply.get_erl_func("lists:map", 2)
Errors include the arity, e.g.
"erlang:length/2 is not exported in erlang (existing arities: 1)".
pub fn get_js_obj(raw_script: String) -> Result(any, String)
Fetch a JavaScript runtime object or function (JavaScript target only).
Takes a dotted path such as "Math.PI" or "console.log".
let assert Ok(3.141592653589793) = apply.get_js_obj("Math.PI")
let assert Ok(max) = apply.get_js_obj("Math.max")
Always returns a runtime error on the Erlang target.
pub fn platform_name() -> String
The current runtime platform.
Returns "erlang" on the Erlang target and "javascript" on the
JavaScript target.
pub fn unwrap(value: any, default: a) -> a
Type-checked fallback for a dynamic value.
Functions fetched from a runtime (get_js_obj / get_erl_func) can return
more than one type: an Int, a Float, a Bool, a String, a list -
or even an abnormal value such as JS undefined/null or Erlang
false/nil/{error, _}. Gleam cannot know which one you got, so it types
the value as any. unwrap/2 settles that:
- same runtime type as
default->value(already of typea) - otherwise ->
default
Type judgement is precise on both runtimes:
true/falseareBool, separated from other atoms (Erlang) and from every other type (JavaScript)IntvsFloat: Erlang distinguishes them natively; JavaScript rounds the value (Math.ceil(value) - value === 0) - anIntyields exactly 0, aFloatnever does because of its precision
let dynamic = apply.call_js("JSON.parse")("123") // any
let int = apply.unwrap(dynamic, 0) // Int or 0
let text = apply.unwrap(dynamic, "") // String or ""