Function Invocation
The primary way to call other functions in Wippy. Execute registered functions synchronously or asynchronously across processes, with full support for context propagation, security credentials, and timeouts. This module is central to building distributed applications where components need to communicate.
Loading
local funcs = require("funcs")
call
Calls a registered function synchronously. Use this when you need an immediate result and can wait for it.
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 string follows the pattern namespace:name where namespace identifies the module and name identifies the specific function.
async
Starts an async function call and returns immediately with a Future. Use this for long-running operations where you don't want to block, or when you want to run multiple operations in parallel.
-- 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 payload, ok = ch:receive()
if ok then
local result = payload:data()
end
| Parameter | Type | Description |
|---|---|---|
target |
string | Function ID in format "namespace:name" |
...args |
any | Arguments passed to the function |
Returns: Future, error
new
Creates a new Executor for building function calls with custom context. Use this when you need to propagate request context, set security credentials, or configure timeouts.
local exec = funcs.new()
Returns: Executor, error
Executor
Builder for function calls with custom context options. Methods return new Executor instances (immutable chaining), so you can reuse a base configuration.
with_context
Adds context values that will be available to the called function. Use this to propagate request-scoped data like trace IDs, user sessions, or feature flags.
-- Propagate request context to downstream services
local exec = funcs.new():with_context({
request_id = ctx.get("request_id"),
feature_flags = {dark_mode = true}
})
local user, err = exec:call("app.api:get_user", user_id)
| Parameter | Type | Description |
|---|---|---|
values |
table | Key-value pairs to add to context |
Returns: Executor, error
with_actor
Sets the security actor for authorization checks in the called function. Use this when calling a function on behalf of a specific user.
local security = require("security")
local actor = security.actor() -- Get current user's actor
-- Call admin function with user's credentials
local exec = funcs.new():with_actor(actor)
local result, err = exec:call("app.admin:delete_record", record_id)
if err and err:kind() == "PERMISSION_DENIED" then
return nil, errors.new("PERMISSION_DENIED", "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. Scopes define the permissions available for the call.
local security = require("security")
local scope = security.new_scope()
local exec = funcs.new():with_scope(scope)
| Parameter | Type | Description |
|---|---|---|
scope |
Scope | Security scope (from security module) |
Returns: Executor, error
with_options
Sets call options like timeout and priority. Use this for operations that need time limits.
-- Set a 5 second timeout for external API call
local exec = funcs.new():with_options({timeout = 5000})
local result, err = exec:call("app.external:fetch_data", query)
if err then
-- Handle timeout or other error
end
| Parameter | Type | Description |
|---|---|---|
options |
table | Implementation-specific options |
Returns: Executor, error
call / async
Executor versions of call and async that use the configured context.
-- Build reusable executor with context
local exec = funcs.new()
:with_context({trace_id = "abc-123"})
:with_options({timeout = 10000})
-- Make multiple calls with same context
local users, _ = exec:call("app.api:list_users")
local posts, _ = exec:call("app.api:list_posts")
Future
Returned by async() calls. Represents an in-progress async operation.
response / channel
Returns the underlying channel for receiving the result.
local future, _ = funcs.async("app.api:slow_operation", data)
local ch = future:response() -- or future:channel()
local result = channel.select {
ch:case_receive(),
timeout:case_receive()
}
Returns: Channel
is_complete
Non-blocking check if the future has completed.
while not future:is_complete() do
-- do other work
time.sleep("100ms")
end
local result, err = future:result()
Returns: boolean
is_canceled
Returns true if cancel() was called on this future.
if future:is_canceled() then
print("Operation was canceled")
end
Returns: boolean
result
Returns the cached result if complete, or nil if still pending.
local value, err = future:result()
if err then
print("Failed:", err:message())
elseif value then
print("Got:", value:data())
end
Returns: Payload|nil, error|nil
error
Returns the error if the future failed.
local err, has_error = future:error()
if has_error then
print("Error kind:", err:kind())
end
Returns: error|nil, boolean
cancel
Cancels the async operation.
future:cancel()
Parallel Operations
Run multiple operations concurrently using async and channel.select.
-- Start multiple operations in parallel
local f1, _ = funcs.async("app.api:get_user", user_id)
local f2, _ = funcs.async("app.api:get_orders", user_id)
local f3, _ = funcs.async("app.api:get_preferences", user_id)
-- Wait for all to complete using channels
local user_ch = f1:channel()
local orders_ch = f2:channel()
local prefs_ch = f3:channel()
local results = {}
for i = 1, 3 do
local r = channel.select {
user_ch:case_receive(),
orders_ch:case_receive(),
prefs_ch:case_receive()
}
if r.channel == user_ch then
results.user = r.value:data()
elseif r.channel == orders_ch then
results.orders = r.value:data()
else
results.prefs = r.value:data()
end
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() |
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 |
| Subscribe failed | errors.INTERNAL |
no |
| Function error | varies | varies |
See Error Handling for working with errors.