Skip to content

Tasks And Scopes

Chris Michael edited this page Sep 2, 2026 · 2 revisions

Pudu

Tasks And Scopes

An async function call creates a task. In the current interpreter, a task is a cold description of work with separate success and failure channels; creating it does not start a background thread.

async fn loadCount() -> Result[Int, Str] {
  Ok(3)
}

Calling loadCount() produces Task[Int, Str]. Awaiting it produces its success value or routes its failure through the enclosing async computation.

Awaiting work

export async fn main() -> Result[Int, Str] {
  let count = loadCount().await
  Ok(count + 1)
}

.await is legal only where async evaluation is permitted. It is not a method looked up on an ordinary value; it is a control boundary understood by the checker and evaluator.

Calls and arguments retain their ordinary left-to-right evaluation order around that boundary. The present evaluator runs the awaited task deterministically rather than scheduling it in parallel.

Structured scopes

export async fn main() -> Result[Int, Str] {
  async with scope {
    let left = loadCount()
    let right = loadCount()
    let first = left.await
    let second = right.await
    Ok(first + second)
  }
}

A task created inside async with scope is registered as a child of that scope. Awaiting a child evaluates it. When control leaves the scope, the interpreter joins registered children that were not explicitly awaited, in creation order. No child is detached from the lifetime of its scope.

This is a task-tree lifetime rule. It should not be confused with a claim of simultaneous execution: the current implementation has no scheduler, worker pool, native thread runtime, or parallel evaluator.

Failure

Task[T, E] records both channels at the type boundary. A child failure is carried by the task and propagated through the enclosing async evaluation according to the interpreter's task-result rules. Pudu does not erase it into an untyped exception.

The postfix ? operator applies to Result and Option, not to a task itself. Await first, then work with the resulting value or the enclosing failure channel dictated by the async signature.

What is not implemented

The following are outside the current contract:

  • spawn and detached tasks;
  • cancellation state, cancellation tokens, or sibling cancellation;
  • parallel execution and scheduling fairness;
  • native Send/Sync enforcement;
  • thread-local storage and synchronization primitives.

The parser reserves parts of the intended surface, but a reserved word is not an execution guarantee. Programs written for the current interpreter should rely only on deterministic task-tree evaluation.

Related

Clone this wiki locally