Basic Capture Blocks

A capture block is a first-class value enclosed in { … }. Positional arguments are accessed via #1, #2, etc. Call a capture with #cap(args) or #cap->invoke(args).

local(add) = { #1 + #2 }
#add(3, 4)           // 7
#add->invoke(10, 5)  // 15

Auto-collect Captures

An auto-collect capture ({^ … ^}) accumulates each produced value into a buffer. Call the capture to retrieve the concatenated result.

local(cap) = {^
  'Hello'
  ', '
  'world'
^}
#cap()                    // 'Hello, world'
#cap->autoCollectBuffer   // same

Closures

Captures close over the local variables in scope at the point they are created:

local(prefix) = 'Hi '
local(greet) = { #prefix + #1 }
#greet('Ada')    // 'Hi Ada'

// captured variable is mutable
local(count) = 0
local(tick) = { #count = #count + 1 }
#tick
#tick
#count    // 2

Associated Captures (inline blocks)

Iteration and control methods accept an associated capture block with =>:

// auto-collect result from loop
loop(3) => {^ loop_count + ' ' ^}
// '1 2 3 '

// forEach with associated block
array('x', 'y', 'z')->forEach => {
  #1->uppercase
}

// early exit via non-local return
define first_even(items) => {
  #items->forEach => {
    #1 % 2 == 0 ? return #1
  }
  return null
}
first_even(array(1, 3, 4, 6))   // 4

Detached Captures and yield / resume

->detach turns a capture into a resumable coroutine. yield suspends execution and returns a value to the caller. Calling the capture again resumes from the next expression after yield.

local(series) = {
  yield 1
  yield 2
  yield 3
}->detach

#series()   // 1
#series()   // 2
#series()   // 3
#series->restart   // reset to start

Locals created before a yield survive the resume. ->restart resets the program counter and restores the creation scope.

Capture Methods

MethodDescription
#cap(args)Call / resume the capture.
#cap->invoke(args)Same as calling directly.
#cap->continueResume without passing a value.
#cap->resumeResume (alias for continue).
#cap->detachDetach and return the capture as a coroutine.
#cap->restartReset to start of capture body.
#cap->homeReturn to the home caller.
#cap->continuationThe underlying resumable continuation.
#cap->autoCollectBufferContents of the auto-collect buffer.
#cap->isDetachedTrue if the capture is detached.
#cap->isResumableTrue if a resume is possible.

Error Handling

protect => { … } establishes an error scope. fail raises an error. handle_failure registers a handler capture that receives the error as #1.

protect => {
  handle_failure => { 'caught: ' + error_msg }
  fail('something went wrong')
}
// 'caught: something went wrong'

Typed failures and filtered handlers

protect => {
  handle_failure(-type='auth') => {
    'auth error: ' + error_msg
  }
  handle_failure => {
    'other error: ' + error_msg
  }
  fail(-type='auth', -code=403, 'access denied')
}
// 'auth error: access denied'

Error context

protect => {
  handle_failure => {
    error_msg    // message string
    error_type   // type tag, if set
    error_code   // numeric code, if set
    rethrow      // re-raise the same error
  }
  fail('boom')
}

currentCapture Metadata

Inside a running capture, currentCapture and currentContinuation expose diagnostic information:

local(cap) = {
  currentCapture->id
  currentCapture->isDetached
  currentCapture->isResumable
  currentCapture->metadata    // source_line, home_method, etc.
}
#cap()