Functions
Actors run one turn at a time — they process a message, then wait for the next. Work that spans multiple turns (network calls, message exchanges, timed sequences) needs a way to deliver results in the future. Callbacks and requestors are how ƿit handles this.
Callbacks
A callback is a function that delivers a result from the future:
function(value, reason)
- On success:
callback(result)— value is the result, reason is absent - On failure:
callback(null, reason)— value is null, reason explains the failure
Callbacks are the primitive. Every async operation in ƿit — send, $delay,
$start, and byte-channel reads and writes — communicates its result through a
callback.
Requestors
A requestor wraps a unit of async work into a composable function. Where a raw callback is fire-and-forget, a requestor can be chained, run in parallel, raced, and cancelled.
function requestor(callback, value)
- callback — called when the work completes, using the callback signature above
- value — input from the previous step or the initial caller
- return — a cancel function (or null if cancellation is not supported)
The key insight: because every requestor has the same shape, they compose. You can build pipelines where the output of one requestor feeds the input of the next, and the whole pipeline is itself a requestor.
Writing a Requestor
var after_one_second = function(callback, value) {
return $delay(function() {
callback(value)
}, 1)
}
A requestor that always succeeds immediately:
var constant = function(callback, value) {
callback(42)
}
A requestor that always fails:
var broken = function(callback, value) {
callback(null, "something went wrong")
}
Cancel Functions
A requestor function may optionally return a cancel function. When the cancel function is called, it attempts to stop the work of the requestor. A cancel function might send a message to some actor informing it that the result is no longer needed. The purpose of cancel is to stop work that is no longer required. It is advisory. It is not an undo. It is not guaranteed, particularly in the case where cancel is called after some actor has completed its work.
Cancel is most effective with the requestor factories that are organizing the work.
A cancel function has this signature:
function cancel(reason, abandon)
- reason — optional, but if included might be logged or propagated
- abandon — if
true, simply stop the requested work. Iffalse, then if possible, complete the assignment, perhaps by calling the callback with the reason
A function as a proxy for a record
A function can stand in for a record. When a function is called as
fn.name(argument_a, argument_b)
it is called as though it had been written
fn("name", [argument_a, argument_b])
The function receives the name of the method and an array of the arguments, and whatever it returns is given to the caller.
var counted = function(name, args) {
return `${name}:${length(args)}`
}
counted.hello() // "hello:0"
counted.add(1, 2) // "add:2"
Pairing with apply
apply(fn, array) calls a function with the elements of an array as its arguments. That
is exactly the shape a proxy receives, so the two compose into something that behaves like
a record of methods:
var methods = {
add: function(a, b) { return a + b },
negate: function(a) { return -a }
}
var calc = function(name, args) {
return apply(methods[name], args)
}
calc.add(2, 3) // 5
calc.negate(7) // -7
calc is an ordinary function, but it is used like a record — and because the dispatch is
code, it can validate names, log calls, forward to another actor, or supply defaults.
apply has a few conveniences worth knowing: if its first argument is not a function it
returns that argument unchanged, and if its second argument is not an array it is used as a
single argument. Passing more arguments than the function accepts halts.
Bracket syntax is accessor syntax for arrays, records, and indexed text reads; it is not
proxy syntax. Accessing a function with [] disrupts.
For language-provided functions such as text, array, format, find, and
round, see Built-in Functions.