Skip to main content

Type Constructors & Aliases

This page explains how to define new types in TypR using type, opaque, and typeconstructor.

Transparent aliases (type)

A type alias creates a transparent name for an existing type. The alias and its underlying type are fully interchangeable:

type Meters <- int;
type Person <- list { name: char, age: int };

# These are equivalent
let d: Meters <- 42;
let d: int <- 42;

Transparent aliases are useful for documentation and clarity — they give semantic meaning to raw types without changing behavior.


Opaque aliases (opaque)

An opaque alias hides the underlying type from external code. This provides stronger encapsulation:

opaque Meters <- int;

let d: Meters <- 42;
let n: int <- d; # ERROR: cannot implicitly convert opaque type

Opaque types enforce a boundary: code outside the module where the alias is defined cannot freely mix the opaque type with its underlying type. This prevents accidental misuse of domain-specific values.


Generic type definitions

Both type and opaque support generic parameters using angle brackets <T>:

type Option`<T>` <- .Some(T) | .None;
opaque Factor`<L>` <- int;
note

Generic parameters use <T> (angle brackets), not [T] (square brackets). Square brackets are reserved for array dimensions and index generics.

Phantom parameters

A phantom parameter appears in the type definition but not in its body. It exists only for type-level tracking:

opaque Factor`<L>` <- int;   # L never appears in the right-hand side

Phantom parameters are useful when you need to distinguish between values that have the same runtime representation but different semantic meanings. The constraint is enforced at compile time through signatures.


Typeconstructors

The typeconstructor keyword registers a generic record constructor that can be used with the TypeName[N]{ ... } syntax:

typeconstructor Tibble[N] record;
typeconstructor Matrix[N, M, T] recursive;

Once registered, you can create parameterized records:

Tibble[3]{ id: int, active: bool }   # record constructor with 3 columns
Tibble[8]{ name: char, score: num } # record constructor with 8 columns

How typeconstructors differ from type aliases

Featuretype / opaquetypeconstructor
PurposeName an existing typeRegister a generic record constructor
Parameters<T> for type parameters[N] for dimension/label parameters
ResultA type aliasA constructor that creates records
Usagelet x: MyType <- ...MyType[N]{ field: T }

See Records & Constructors for how constructor calls work with spread operators and named embedding.


The {...} rule

A { ... } block following a parameterized type name always makes it a record constructor:

Tibble[3]{ id: int, active: bool }   # record constructor
Tibble[3] # just a parameterized alias (no braces)

This distinction is important — the presence or absence of braces changes the semantics entirely.


Relationships with other features

Type constructors and aliases interact with several other TypR features:

  • Generics & Kind Sigils — Generic parameters (<T>) and kind sigils (#N, %R, etc.) work together to constrain type parameters
  • Signatures@extern and @signature use type aliases to declare external function types
  • Foreign valuesopaque Foreign<T> is the idiomatic way to wrap external R values

Summary

KeywordPurposeGeneric syntaxExample
typeTransparent alias<T>type Meters <- int
opaqueOpaque alias<T>opaque Meters <- int
typeconstructorGeneric record constructor[N]typeconstructor Tibble[N] record