Skip to content

Named Types and Unions

Chloë Strainge edited this page Oct 14, 2026 · 1 revision

Named types and unions

type introduces a distinct identity backed by a type expression. It is not a transparent alternate spelling.

type Meters = Float
let $near: Meters = 3
let $far = Meters(4)
let $total = $near + $far
echo $total $total.type Float($total)
7.0 Meters 7.0

Annotations wrap compatible values; explicit construction can do the same. Unwrapping is explicit. Same-type numeric arithmetic preserves the named type; mixing named types or a named and unnamed number needs a deliberate conversion. Inherited methods retain their own declared results.

Nested definitions such as type RouteLength = Meters are allowed. Named arrays, hashes, and structs inherit their underlying operations while keeping identity. Items yielded from named collections are the underlying collection's items.

Union constraints

let $label: Integer | String = 7
$label = "seven"
let $maybe: Integer | ()
echo $label $maybe
type Selection = Integer | String | ()
let $selected: Selection
echo $selected.type
seven ()
Selection

An existing union-member match preserves the value. Otherwise implicit wrapping or Integer-to-Float promotion must have one unambiguous result. Unions admitting () default to unit; others need initialization. A named union remains a named value, including when it contains unit.

Interfaces on unions

A named union can define methods without attaching them to every member globally. An annotation or explicit conversion selects that interface. Exact type narrowing selects the concrete member's methods inside a branch.

Marked error unions are especially useful for library error interfaces. They keep the concrete error payload's .type while exposing the union's selected methods. Normal named unions retain their nominal .type. See Errors and cleanup for propagation and handling.

error struct BadLabel { pub detail: String }
error struct NoCapacity { pub detail: String }
type PackingError = BadLabel | NoCapacity
fn PackingError::message(): String
{
    match $self.type
    {
        BadLabel   => { "label: " + $self.detail }
        NoCapacity => { "capacity: " + $self.detail }
    }
}
try { let $problem: PackingError = BadLabel(detail: "missing postcode") }
catch ($problem: PackingError) { echo $problem.message $problem.type }
label: missing postcode BadLabel

Recursive aliases are rejected. Struct recursion through optional or collection fields can model linked structures without requiring an impossible fully initialized cycle.


Related: Conversions · Function types · Errors

Clone this wiki locally