Invisible link to canonical for Microformats

ADR-026 Select Case Values, Await Integration, and Whole-Value Match Binding


Status

Accepted

Context

ADR-025 establishes select as an expression composed of cases, each producing a case value that is passed into an explicit body via the /> operator.

As usage has evolved, several related ergonomics issues have emerged:

  • Many select expressions only need the selected case value, making the body redundant.
  • Task timeouts are currently expressed using a special await <task> within <duration> form, even though select already models coordination.
  • Call-chain style flows (recv … /> match { … }) often need access to both a destructured value and the original value, forcing unnecessary reconstruction.

All three issues stem from the same root cause: existing semantics are expressive, but not directly exposed in a composable, ergonomic way.

Decision

Optional select case bodies

A select case may omit the /> body.

When omitted, the select expression evaluates to the case value of the selected case, as defined in ADR-025.

A body-less case is semantically equivalent to a body that returns its input unchanged.

select {
  recv c
  _
}

If the _ case is selected, the result of the select expression is nil.

Readiness, fairness, and cancellation semantics are unchanged.


Await as a select case

select gains a new case header form:

await <taskExpr>

The case becomes ready when the task completes and any unselected tasks are cancelled.

The case value is the result of awaiting the task. If the task fails, selecting this case throws the same runtime error as the current await <taskExpr>.

This allows task coordination and timeouts to be expressed uniformly using select.

select {
  await h1
  await h2
  after 500 /> fn(t) { throw Error{type:"Timeout"} }
}

This pattern enables racing tasks and waiting for the first to complete.

CLARIFICATION: after configured with 0 is ignored and never selected, use _ instead.

The special form:

await <task> within <duration>

is replaced by this composition and the both await and within forms are removed.


Whole-value binding in match arms

match arms may bind the entire matched value while also applying a pattern using the form:

<ident> @ <pattern> => <expr>

If the pattern matches, <ident> is bound to the original scrutinee value for that arm.

This is especially useful in call-chain expressions where destructuring is needed for control flow, but the original value should be returned or forwarded unchanged.

recv c1 /> match {
  box @ Full{value: 100} => box
  _                      => :done
}

Whole-value bindings are scoped to the match arm and do not affect match ordering or exhaustiveness.

Consequences

Positive

  • Reduces syntactic noise in common select patterns
  • Makes ADR-025 case values directly observable and usable
  • Eliminates special-case await timeout syntax
  • Enables clean task racing via select
  • Improves call-chain ergonomics without introducing new primitives
  • Fully backward-compatible with existing semantics

Negative

  • A failed awaited task may be selected fairly alongside other ready cases, resulting in immediate error propagation

Neutral

  • No changes to fairness, readiness, or cancellation rules
  • Preference and ordering must continue to be expressed structurally