Function Invocation
The funcs module calls registered functions synchronously or asynchronously. An executor can propagate request context, security identity, and implementation-specific call options. This page is an API reference; target IDs, arguments, and application data represent surrounding code.
Loading
local funcs = require("funcs")
call
Calls a registered function synchronously and waits for its result.
local result, err = funcs.call("app.api:get_user", user_id)
if err then
return nil, err
end
print(result.name)
| Parameter | Type | Description |
|---|---|---|
target |
string | Function ID in format "namespace:name" |
...args |
any | Arguments passed to the function |
Returns: result, error
The target uses the namespace:name format.
async
Starts a function call and returns a Future immediately. Futures allow other work to continue while the call runs and support multiple concurrent calls.
-- Start heavy computation without blocking
local future, err = funcs.async("app.process:analyze_data", large_dataset)
if err then
return nil, err
end
-- Do other work while computation runs...
-- Wait for result when ready
local ch = future:response()
local _, open = ch:receive()
if not open then
return nil, errors.new("future response channel closed")
end
local payload, result_err = future:result()
if result_err then
return nil, result_err
end
local result, data_err = payload:data()
if data_err then return nil, data_err end
| Parameter | Type | Description |
|---|---|---|
target |
string | Function ID in format "namespace:name" |
...args |
any | Arguments passed to the function |
Returns: Future, error
new
Creates an Executor for calls that need custom context, security identity, or call options.
local exec = funcs.new()
Returns: Executor
Executor
An executor stores call context and options. Its configuration methods return new executor instances, allowing a base configuration to be reused.
with_context
Adds request-scoped values that will be available to the called function, such as trace IDs, session data, or feature flags.
local ctx = require("ctx")
-- Propagate request context to downstream services
local request_id, ctx_err = ctx.get("request_id")
if ctx_err then return nil, ctx_err end
local exec, err = funcs.new():with_context({
request_id = request_id,
feature_flags = {dark_mode = true}
})
if err then return nil, err end
local user, err = exec:call("app.api:get_user", user_id)
if err then return nil, err end
| Parameter | Type | Description |
|---|---|---|
values |
table | Key-value pairs to add to context |
Returns: Executor, error
with_actor
Sets the security actor used for authorization checks in the called function.
local security = require("security")
local actor = security.actor() -- Get current user's actor
-- Call admin function with user's credentials
local exec, err = funcs.new():with_actor(actor)
if err then return nil, err end
local result, err = exec:call("app.admin:delete_record", record_id)
if err and err:kind() == errors.PERMISSION_DENIED then
return nil, errors.new({kind = errors.PERMISSION_DENIED, message = "User cannot delete records"})
end
| Parameter | Type | Description |
|---|---|---|
actor |
Actor | Security actor (from security module) |
Returns: Executor, error
with_scope
Sets the security scope for called functions. The scope defines the permissions available to the call.
local security = require("security")
local scope = security.new_scope()
local exec, err = funcs.new():with_scope(scope)
if err then return nil, err end
| Parameter | Type | Description |
|---|---|---|
scope |
Scope | Security scope (from security module) |
Returns: Executor, error
with_options
Sets call options such as the retry policy or the overlay network. Options are merged over any preset options of the target function entry.
-- Retry transient failures up to 5 times with exponential backoff
local exec = funcs.new():with_options({
retry = { max_attempts = 5, initial_delay = 100 }
})
local result, err = exec:call("app.external:fetch_data", query)
if err then
-- All attempts failed, or the error was not retryable
end
| Parameter | Type | Description |
|---|---|---|
options |
table | Call options |
| Option | Type | Description |
|---|---|---|
retry.max_attempts |
int | Maximum attempts including the first (1 disables retry) |
retry.initial_delay |
int/duration | Delay before first retry (ms or duration string), default 100 |
retry.max_delay |
int/duration | Upper bound for the backoff delay (ms or duration string), default 10s |
retry.backoff_factor |
number | Multiplier applied to the delay after each attempt, default 2.0 |
retry.jitter |
number | Random jitter fraction applied to each delay, default 0.1 |
retry.retry_kinds |
string[] | Only retry errors of these kinds; by default every kind except Invalid, PermissionDenied and Internal is retried |
retry.skip_kinds |
string[] | Never retry errors of these kinds |
network |
string | Registry ID of an overlay network to route the call's outbound traffic through; requires the network.select permission |
Only retryable errors trigger retries; non-retryable errors surface immediately. Temporal activity options are described in Activities.
The runtime-defined option is:
| Recognized option | Type | Description |
|---|---|---|
network |
string | Registry ID of the outbound network.* entry |
Returns: Executor, error
Selecting a network requires network.select permission on that network ID.
call and async
The executor versions of call and async use its configured context and options.
-- Build reusable executor with context
local exec = funcs.new()
:with_context({trace_id = "abc-123"})
:with_options({retry = {max_attempts = 3}})
-- Make multiple calls with same context
local users, users_err = exec:call("app.api:list_users")
if users_err then return nil, users_err end
local posts, posts_err = exec:call("app.api:list_posts")
if posts_err then return nil, posts_err end
Future Invocation Summary
async() returns a future representing an in-progress invocation. The methods below cover the caller-facing steps for receiving, inspecting, or canceling that invocation. See Future for the Future object reference.
response and channel
Returns the channel used to receive the result.
local time = require("time")
local future, err = funcs.async("app.api:slow_operation", data)
if err then
return nil, err
end
local ch = future:response() -- or future:channel()
local timeout, err = time.after("5s")
if err then
return nil, err
end
local result = channel.select {
ch:case_receive(),
timeout:case_receive()
}
Returns: Channel
The response channel signals completion. After it becomes ready, call future:result() to obtain the cached value or the called function's error.
is_complete
Checks whether the future has completed without blocking.
while not future:is_complete() do
-- do other work
local _, sleep_err = time.sleep("100ms")
if sleep_err then return nil, sleep_err end
end
local result, err = future:result()
Returns: boolean
is_canceled
Returns true if the future has been marked canceled by its provider. See the cancellation limitation below.
if future:is_canceled() then
print("Operation was canceled")
end
Returns: boolean
result
Returns the cached result when complete or nil while the operation is pending.
local value, err = future:result()
if err then
print("Failed:", err:message())
elseif value then
local data, data_err = value:data()
if data_err then return nil, data_err end
print("Got:", data)
end
Returns: Payload|table|nil, error|nil
error
Returns the operation error when the future has failed.
local err, has_error = future:error()
if has_error then
print("Error kind:", err:kind())
end
Returns: error|nil, boolean
This method returns a non-retryable INTERNAL wrapper for a failed operation. Use result() to preserve the called function's original error metadata.
cancel
Requests cancellation of the asynchronous operation.
local canceled, err = future:cancel()
if err then return nil, err end
Returns: boolean, error
cancel() and is_canceled() are not a stable cross-provider contract. Do not use cancellation for application correctness; time out locally and ignore a late result until the runtime separates provider cancellation.
Parallel Operations
Combine async with channel.select to run and collect multiple calls concurrently.
-- Start multiple operations in parallel
local f1, err = funcs.async("app.api:get_user", user_id)
if err then return nil, err end
local f2, err = funcs.async("app.api:get_orders", user_id)
if err then return nil, err end
local f3, err = funcs.async("app.api:get_preferences", user_id)
if err then return nil, err end
-- Wait for all to complete using channels
local user_ch = f1:channel()
local orders_ch = f2:channel()
local prefs_ch = f3:channel()
local pending = {
[user_ch] = {name = "user", future = f1},
[orders_ch] = {name = "orders", future = f2},
[prefs_ch] = {name = "preferences", future = f3}
}
local results = {}
while next(pending) do
local cases = {}
for ch in pairs(pending) do
cases[#cases + 1] = ch:case_receive()
end
local r = channel.select(cases)
local completed = pending[r.channel]
pending[r.channel] = nil
local payload, result_err = completed.future:result()
if result_err then
return nil, result_err
end
local data, data_err = payload:data()
if data_err then
return nil, data_err
end
results[completed.name] = data
end
Permissions
Function operations are subject to security policy evaluation.
| Action | Resource | Description |
|---|---|---|
funcs.call |
Function ID | Call a specific function |
funcs.context |
context |
Use with_context() to set custom context |
funcs.security |
security |
Use with_actor() or with_scope() |
network.select |
Network ID | Use with_options({network = ...}) to select an overlay network |
Errors
| Condition | Kind | Retryable |
|---|---|---|
| Target empty | errors.INVALID |
no |
| Namespace missing | errors.INVALID |
no |
| Name missing | errors.INVALID |
no |
| Permission denied | errors.PERMISSION_DENIED |
no |
| Async outside a process | errors.INTERNAL |
no |
| Subscribe failed | errors.INTERNAL |
no |
| Async start dispatch failed | errors.INTERNAL |
no |
| Function error | varies | varies |
See Error Handling for working with errors.