Skip to content

RFC - RFC 039: race for Awaitable Concurrency #173

@dannymeijer

Description

@dannymeijer

Area

Incan Language (syntax/semantics), Compiler (frontend/backend), Stdlib (std.async)

Summary

Introduce race as an import-activated std.async vocabulary form for "first-completion wins" concurrency, together with an Incan-native Awaitable[T] protocol that formalizes what await means in generic code. The architecture is deliberately layered: await remains a core language feature, Awaitable[T] is the Incan-facing protocol behind it, and race is import-activated vocabulary that desugars to std.async helper calls.

RFC document: 039_race_for_awaitable_concurrency.md

Motivation

The stdlib currently lacks a clean way to express generic awaitables. A timeout helper should be straightforward Incan — pub async def timeout_option[T, F with Awaitable[T]](seconds: float, task: F) -> Option[T] — but today that contract cannot be expressed and preserved cleanly through the frontend and lowering pipeline. This RFC closes that gap.

Proposal sketch

# Basic race — first awaitable to complete wins, loser is cancelled
result = race for value:
    await fast() => value
    await slow() => value

# Union result (RFC 029) — branches may produce different types
result: str | int = race for value:
    await fetch_text() => value
    await fetch_count() => value

# Timeout becomes ordinary std.async composition
pub async def timeout_option[T, F with Awaitable[T]](seconds: float, task: F) -> Option[T]:
    return race for value:
        await task => Some(value)
        await sleep(seconds) => None

# Direct helper use (the desugaring target)
result = await std.async.race(
    std.async.arm(fast(), (value) => value),
    std.async.arm(slow(), (value) => value),
)

Semantic layering

  1. Core: await and Awaitable[T]await expr is valid only if expr satisfies Awaitable[T], result type is T
  2. Library: RaceArm[R], arm(awaitable, on_win), race(*arms: RaceArm[R]) in std.async
  3. Vocabulary: race for value: syntax — import-activated via import std.async, desugars to the library layer

Key design decisions

  • race is not selectSELECT is reserved for future query language surfaces; Go-style select is channel-oriented while this RFC is about arbitrary awaitables
  • race is not async matchmatch inspects a value that already exists; race waits on several awaitables and cancels losers
  • Cancellation is cooperative; losing arms are dropped, triggering their normal cleanup
  • Tie-breaking in v1: first arm in source order wins (deterministic)
  • Awaitable[T] follows RFC 028's language-first philosophy — specified in Incan terms, mapped to Rust futures in the backend
  • The variadic helper shape race(*arms: RaceArm[R]) depends on RFC 038; fixed-arity helpers are an acceptable bridge

Unresolved questions

  1. Should Awaitable[T] be user-implementable in v1, or compiler-recognized only?
  2. Should std.async.select be renamed to std.async.race with compatibility exports?
  3. Does RFC 027 need a dedicated expression-block surface kind for race for value:?
  4. Should a later version add a more general pattern-binding race form, or is race for value: plus ordinary match sufficient?
  5. Should a later version add default arms, guard expressions, or unbiased scheduling options?

Layers affected

  • Core language / TypecheckerAwaitable[T] builtin protocol; await type-checking; bound lowering for F with Awaitable[T]
  • Parser / Vocabularyrace for value: import-activated syntax via RFC 027 vocabulary/desugaring machinery
  • IR Lowering — desugar race for value: to std.async.race(std.async.arm(...), ...) calls
  • Stdlib (std.async)RaceArm[R], arm(...), race(...) helpers; rewrite std.async.select placeholders
  • Rust backendAwaitable[T] → Rust future semantics; race(...)tokio::select! or equivalent

Related RFCs

  • RFC 023 (Compilable stdlib & Rust module binding)
  • RFC 027 (Vocabulary/desugaring — activation mechanism for race)
  • RFC 028 (Trait-based operator overloading — Awaitable[T] follows the same language-first pattern)
  • RFC 029 (Union types — natural return type when race arms produce different types)
  • RFC 035 (First-class function references — named handlers work as arm(...) callbacks)
  • RFC 038 (Variadic args — the ideal race(*arms: RaceArm[R]) helper shape)

Checklist

  • I checked for an existing RFC/issue covering this.
  • I can describe how this impacts existing code and how to migrate (if needed).

Metadata

Metadata

Assignees

Labels

featureNew feature or requestincan language semanticsSuggestions, features, or bugs related to the Incan Language itself (syntax and semantics)

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions