Skip to content

Type Definitions

Ceramic has four kinds of user-defined types:

Records

A record is a general-purpose aggregate laid out in memory like a C struct.

record Point (x:Int, y:Int);
Records may be parameterized. When no predicate is needed, the pattern guard is optional: unrecognized names in brackets are taken as unbounded pattern variables:

record Point[T] (x:T, y:T);          // [T] guard is implied

[T | Float?(T)]
record FloatPoint[T] (x:T, y:T);     // explicit predicate

Computed Layouts

A record's layout can be computed from an expression that evaluates to a list of [fieldName, fieldType] pairs:

// Equivalent to: record Point[T] (x:T, y:T)
record Point[T] = [[#"x", T], [#"y", T]];

// Custom coordinate names
record PointWithCoordinates[T, xy] = [[xy.0, T], [xy.1, T]];
This pattern also enables template specialization via an overloaded helper function:

record Vec3D[T] = Vec3DBody(T);

private define Vec3DBody;
[T | T != Double]
overload Vec3DBody(static T) = [[#"coords", Array[T, 3]]];
overload Vec3DBody(static Float) = [[#"coords", Vec[Float, 4]]];  // SIMD path

Variants

A variant is a discriminated union. A variant value holds exactly one of its instance types and knows which one at runtime, enabling type-safe dynamic dispatch.

variant Fruit (Apple, Orange, Banana);
Variants may be parameterized (pattern guard optional when no predicate is needed):

variant Maybe[T] (Nothing, T);          // [T] implied
variant Either[T, U] (T, U);           // [T, U] implied

[C | Color?(C)]
variant Fruit[C] (Apple[C], Orange[C], Banana[C]);
The instance list may be any expression evaluated at compile time:

private RainbowTypes(Base) =
    Base[Red], Base[Orange], Base[Yellow], Base[Green],
    Base[Blue], Base[Indigo], Base[Violet];

variant Fruit (..RainbowTypes(Apple), ..RainbowTypes(Banana));

Extending Variants

Variants are open. New instance types are added with instance:

variant Exception ();

record RangeError (lowerBound:Int, upperBound:Int, attemptedValue:Int);
record TypeError  (expectedTypeName:String, attemptedTypeName:String);
instance Exception (RangeError, TypeError);
instance binds to variants by pattern matching, so parameterized variants can be extended selectively:

[C | Color?(C)]
variant Fruit[C] ();

instance Fruit[Yellow] (Banana);          // only Yellow

[C | C == Red or C == Green]
instance Fruit[C] (Apple);               // Red and Green only

[C]
instance Fruit[C] (Berry[C]);            // all Fruit[C]
The pattern guard on instance is not optional for generic extension. Without it, instance Variant[T] attempts to match only the concrete type Variant[T] (where T must already be defined), not all parameterized instances.

Enumerations

An enumeration defines a type whose values are one of a fixed set of named constants. The constant names are defined in the current module with the same visibility as the type.

enum ThreatLevel (Green, Blue, Yellow, Orange, Red, Midnight);

private enum SecurityLevel (
    Angel_0A, Archangel_1B,
    Principal_2C, Power_3D,
    Virtue_4E, Domination_5F,
    Throne_6G, Cherubic_7H,
    Seraphic_8X,
);
Enumerations cannot currently be parameterized and do not allow pattern guards.

Lambda Types

Lambda types are record-like types that capture values from their enclosing scope. They are created implicitly by the compiler when lambda expressions capture variables: there is no explicit syntax for defining them.