Overview

The metadef statement declares a new named type, essentially assigning a type expression to a name.

metadef name:type_expression. 

Parameterised Types (Type Functions)

A metadef can declare type parameters with the takes keyword, producing a parameterised type alias — in effect a type function. The parameters are named after takes (comma-separated) and may be used anywhere inside the body’s type expression:

metadef vector takes T: T[].
metadef pair takes A, B: [A, B].

A parameterised alias is applied by writing it followed by its type arguments (juxtaposition), anywhere a type expression is valid. Applying it substitutes the arguments for the parameters and reduces to the resulting type:

metadef vector_int: vector int.        // in the body of another metadef

A IS vector int = [1, 2, 3].           // in a binding's type annotation
P IS pair int str = [42, "hello"].

The reduced type is identical to writing the desugared form by hand — for example vector int is the same type as int[], and pair int str is the same type as [int, str].

Parameterised aliases compose — one may appear inside another’s body:

metadef vector takes T: T[].
metadef matrix takes T: vector (vector T).   // a 2-D array of T

M IS matrix int = [[1, 2, 3], [4, 5, 6]].

Type-level reduction runs at compile time and is bounded so that a non-terminating or pathological type function fails cleanly rather than hanging. Reduction is limited to a nesting depth of 64 and a total of 1,048,576 reduction steps; exceeding either limit is a compile-time error. Legitimate deeply-nested types well within these bounds compile normally.

A few constructs are rejected at compile time:

  • takes parameters cannot be combined with type destructuring in the same metadef.
  • A takes parameter name cannot be repeated.

Type Destructuring in metadef

The record and tuple type expressions support the destructuring syntax, allowing you to declare multiple types in one metadef statement.

metadef t_person: (
    $name->t_name: (
        $fname->t_first_name: str,
        $lname->str
    ),
    $address->t_addr: (
        $number->str,
        $street->str,
        $code->int
)).
metadef t_person: (
    $name->t_name: (
        $fname->t_first_name: str,
        $lname->str
    ),
    $address->t_addr: (
        $number->str,
        $street->str,
        $code->int
)).

The example declares these types in one statement:

  • t_first_name as string
  • t_name as a record with two string fields $fname and $lname
  • t_addr as a record with three fields, representing the address
  • t_person as a record of two fields, each of which is also a record, as shown.

Type destructuring is especially convenient in the specification of messaging protocols and database tables. Each row in a table could be a record of records (with, possibly, several levels of records). Declaring all of the involved types in one statement saves a lot of typing.