8.4 Chains
A chain links asynchronous steps so that either the whole chain is executed or the first unhandled error is returned.
let page = Users.get(id)
-> (Orders.recent(it.id), Recs.for(it.id))
-> (orders, recs) => Page{ orders, recs }
~> Audit.log("page", id)
!> Timeout => Page.empty()
within PAGE_DEADLINE
retry 2
let html = await pageSyntax: head ( link )* ( suffix )*, where a link is -> step, ?-> step, ~> step, !> pattern => handler; a suffix is within e, retry e (on Types)? (backoff e)?.
The head is an expression of type Task<T, E>, a call of a suspendable function (lifted to a Task by §6.3) or any other expression (a ready value).
A step is one of:
- an expression that uses
it— the value of the previous step; - a lambda
x => eor one with destructuring(a, b) => e; - a tuple
(e1, …, en)— fan-out: eacheiis evaluated as a separate task in parallel (itis available insideei); the result is a tuple of values; the first error cancels the others.
The result of a step that is a call of a suspendable function or a Task is awaited. If the result type contains errors and the step returned an error, the remaining links to the right are skipped up to the nearest matching !>.
| Link | Semantics |
|---|---|
-> step | execute the step over the value on the left |
?-> step | if the value on the left is none, the result of the chain is none (none is added to the type), otherwise execute the step |
~> step | start the step in the background; the value of the chain stays the same; step errors are written to the log and not propagated; the background task lives in the node's background scope (§8.5) |
!> P => h | catches an error matching pattern P from the whole part of the chain to the left; the value of h replaces the value of the left part and execution continues with the links to the right; h may fail |
within d | a deadline for the whole part to the left; exceeding it is the error Timeout (which a !> to the right can catch) |
retry n | repeat the whole part to the left up to n times on the errors Timeout | Unavailable (or those listed in on); the default backoff is exp(100ms, 10s) with jitter |
Chain type — Task<T, E>, where T is the type of the last value (with none if there is a ?->), E is the union of the errors of all steps minus those caught by !> plus the errors of handlers, plus Timeout with within.
Starting: a chain is lazy. It is started by await, spawn, passing to a lazy parameter or use in statement position (then it is awaited implicitly, like a call of a suspendable function).
it refers to the nearest enclosing chain; in a nested chain the outer value has to be named with a lambda.
Related diagnostics
See also
Source: docs/spec/oxl-standard.en.md