Records

Defining record types

A record type is declared with fields in an intented block, each on their own line.

main void() a point = 1, 2 log a.x b point = x: 1, y: 2 log b.y point record x float y float

Defining a record implicitly defines a new function.
In the above example, its signature would be: new point(x float, y float)
Like any new function, it can be called with either 1, 2 or x: 1, y: 2.
The record also implicitly defines functions for getting each field.
In the above example, these would have signatures of x float(a point) and y float(a point).

Short record syntax

For simple records, you can collapse the definition onto one line.

point record(x float, y float)

Empty records

The fields can be omitted, resulting in an empty type.

empty record

Arguments records

For functions with many arguments, you may want to make them a record:

main void() log foo x: 1, y: 2 foo-args record(x nat, y nat) foo nat(args foo-args) args.x + args.y

Mutable records

Any record fields marked with mut are mutable. As with mutable locals, the mut goes before the type.

main void() a mut-point = 1, 2 a.x := 3 log a.x mut-point record mut x mut float y mut float

a.x := 3 is a syntax equivalent to calling set-x a, 3.

Mutable record fields implicitly define setter functions.
In this case, they are set-x void(a point, value float) and set-y void(a point, value float).

Defining "new"

Since new functions are just ordinary functions, you can define additional new overloads.

main void() a point = () log a.x point record(x float, y float) new point() # This calls 'new point(x float, y float)' 0, 0

Named constructors

Defining a record also implicitly creates a function with the same name as the record and the same signature as new.

main void() log point 1, 2 point record(x float, y float)

point 1, 2 looks better than (1, 2)::point.
But if the type annotation is not necessary, prefer to write just 1, 2.

Other implicit functions

Declaring a record type point also implicitly declares:

main void() a point = 1, 2 log a == a log a <=> (2, 1) log deterministic-hash a # Uses 'hash' log a # Uses 'to json' log a.to::json.to::point == a point record(x float, y float)

These functions are only defined for immutable records with no private fields.

It is possible to define these functions explicitly yourself.
However, ==, <=>, and hash are unsafe to define yourself, so the body must be wrapped in trusted. (See Unsafe code.)
The reason they are unsafe is that badly written comparison or hash functions can break maps.
json conversion functions are safe to define yourself, but make sure a round trip (a.to::json.to::point) is the identity function.

If they were defined explicitly, they would look like this:

main void() a point = 1, 2 log a == a log a <=> (2, 1) log deterministic-hash a # Uses 'hash' log a # Uses 'to json' log a.to::json.to::point == a point record(x float, y float) == bool(a point, b point) trusted a.x == b.x and a.y == b.y <=> comparison(a point, b point) trusted compare-x = a.x <=> b.x if compare-x == equal then a.y <=> b.y else compare-x hash void(a point, state hash-state) trusted hash a.x, state hash a.y, state to json(a point) x: a.x.to y: a.y.to to point(a json) x: a["x"].to y: a["y"].to