Language Policy

Types

Canonical grammar, semantics, constraints, and deferred boundaries for Types.

The type system combines exact primitives, literal and union types, structural records, function types, standard generic constructors, and closed nominal declarations without implicit conversions.

Policy

Primitive types are int, float, string, bool, and unit (). Standard constructors are Array<T>, Map<K,V>, Set<T>, Option<T>, and Result<T,E>. A type declaration is a transparent alias, including generic aliases; it does not create nominal identity. Function types use (T) -> U, with a final variadic entry written ...T. Nullable values are ordinary unions containing null.

Specified behavior

Literal types restrict a value to one literal and unions accept any member. Type parameters are invariant. Enum, record, and newtype declarations create distinct nominal identities; their construction, matching, comparison, JSON shape, defaults, derives, protocols, and receiver methods follow their own bounded rules. Context supplies missing type information for polymorphic empties such as None, [], and generic constructors.

Constraints and loud decline

The checker rejects [T], function(T) -> U, non-final variadics, recursive alias cycles, incompatible union operations, and unsolved generic empties. Generic bounds exist only on named function type parameters and only for the admitted static protocols. There is no structural subtyping promise, implicit int/float conversion, variance annotation, higher-rank type, or generic lambda binder.

Deferred boundary

Higher-rank polymorphism, variance, recursive aliases, generic bounds on aliases or nominal declarations, open schemas, broad reflection, and broader protocol constraints remain deferred. Explicit type arguments are limited to exact admitted rank-1 callable heads; partial or decorative type argument lists are rejected.

Worked example

TOPAZ
type TrafficLight = "red" | "yellow" | "green"
type Pair<T> = { first: T, second: T }

function next(light: TrafficLight) -> TrafficLight {
    match light {
        case "red" => "green"
        case "green" => "yellow"
        case "yellow" => "red"
    }
}

let bounds: Pair<int> = { first: 0, second: 100 }