Function Invocation
Function Invocation
Section titled “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
Section titled “Loading”local funcs = require("funcs")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, errendprint(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.
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 blockinglocal future, err = funcs.async("app.process:analyze_data", large_dataset)if err then return nil, errend
-- Do other work while computation runs...
-- Wait for result when readylocal 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
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
Executor
Section titled “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
Section titled “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 serviceslocal 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
Section titled “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 credentialslocal exec = funcs.new():with_actor(actor)local result, err = exec:call("app.admin:delete_record", record_id)if err and err:kind() == errors.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
Section titled “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
Section titled “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 calllocal exec = funcs.new():with_options({timeout = 5000})local result, err = exec:call("app.external:fetch_data", query)if err then -- Handle timeout or other errorend| Parameter | Type | Description |
|---|---|---|
options | table | Implementation-specific options |
Returns: Executor, error
call / async
Section titled “call / async”Executor versions of call and async that use the configured context.
-- Build reusable executor with contextlocal exec = funcs.new() :with_context({trace_id = "abc-123"}) :with_options({timeout = 10000})
-- Make multiple calls with same contextlocal users, _ = exec:call("app.api:list_users")local posts, _ = exec:call("app.api:list_posts")Future
Section titled “Future”Returned by async() calls. Represents an in-progress async operation.
response / channel
Section titled “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
Section titled “is_complete”Non-blocking check if the future has completed.
while not future:is_complete() do -- do other work time.sleep("100ms")endlocal result, err = future:result()Returns: boolean
is_canceled
Section titled “is_canceled”Returns true if cancel() was called on this future.
if future:is_canceled() then print("Operation was canceled")endReturns: boolean
result
Section titled “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())endReturns: Payload|nil, error|nil
Returns the error if the future failed.
local err, has_error = future:error()if has_error then print("Error kind:", err:kind())endReturns: error|nil, boolean
cancel
Section titled “cancel”Cancels the async operation.
future:cancel()Returns: boolean, error
Parallel Operations
Section titled “Parallel Operations”Run multiple operations concurrently using async and channel.select.
-- Start multiple operations in parallellocal 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 channelslocal 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() endendPermissions
Section titled “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
Section titled “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.