Skip to content

Projections

Refer to inferred function result, error, and local binding types.

Updated View as Markdown

A type projection names a type computed from a function declaration.

Form Meaning
function::result Success or ordinary return type
function::err Error type
function::name Useful data type of the local binding named name

Projections are written only in type positions.

They are compile-time references, not runtime reflection. The compiler waits until the target function’s signature and body types are finalized, then replaces the projection with the resolved type.

Results

::result refers to the function’s successful return type.

fn load_numbers(): []int {
    return [1, 2, 3]
}

struct Cache {
    values: load_numbers::result
}

The projection resolves after return inference. It therefore works for an explicit return and for a function whose result is inferred.

For a fallible function, ::result unwraps the success side.

fn load_user(id: int): User ! LoadError {
    // ...
}

type LoadedUser = load_user::result

LoadedUser is User, not Result[User, LoadError].

Errors

::err names a function’s error type.

type UserLoadError = load_user::err

Applying ::err to an infallible function is an error because there is no error side to project.

This is useful for adapters that should track a lower-level API’s error contract without manually duplicating a moving union.

For a declared or inferred error union, ::err denotes the complete union, not the error type of only the first failing call.

Bindings

function::name projects a named local binding from the function.

model User {
    id: int
    name: string
}

fn fetch_users(): []User ! QueryError {
    rows := FROM User?
    return rows
}

type Row = fetch_users::rows

A binding projection peels a list or optional wrapper to expose the useful element shape. In the example, rows has type []User, while fetch_users::rows resolves to User.

The current peeling rules are:

Binding type Projected type
Result[T, E] first continue with T
Option[T] or T? T
List[T] or []T T
Set[T] T
Map[K, V] V
another type T T unchanged

After peeling Result, one collection or optional wrapper is removed. A map projects its value shape, not its key or (K, V) entry shape.

This behavior is designed for query-generated records.

fn summaries(): void ! QueryError {
    rows := FROM User SELECT { id, name }?
}

type Summary = summaries::rows

Summary names the compiler-generated { id, name } row shape without copying its definition.

Qualified query fields

A projection generated from a join retains the types resolved from each qualified model field.

fn capacities(): void ! QueryError {
    rows := FROM Booking
        JOIN Schedule ON Booking.schedule_id == Schedule.id
        SELECT { capacity: Schedule.capacity }?
}

type Capacity = capacities::rows

The compiler resolves Schedule.capacity against Schedule, then records that field in the generated record.

Failures

The compiler diagnoses:

  • an unknown function;
  • a missing binding name;
  • ::err on an infallible function;
  • a projection used where a value is expected;
  • a projected type that cannot be finalized because the source declaration itself is invalid.

Binding lookup applies to a named local binding in the target body. A destructured tuple or record does not provide one aggregate binding name to project. Name the component explicitly or return a named type when it is part of a stable contract.

Coupling

A projection intentionally couples one declaration’s type to another function’s inferred body or query shape. This is useful inside a module, but it can make a broad public API sensitive to local renaming.

Use a named struct for a durable external contract. Use a projection when tracking the compiler-derived shape is the desired behavior.

Changing the target’s return annotation, fallibility, local name, collection wrapper, or query projection can change consumers at compile time. That sensitivity is the purpose of the feature, but it is also why projections are best kept within a closely maintained module boundary.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close