Skip to content

Define and consume sum, record, and opaque types

Goal: declare each of Bynk’s three composite type kinds and use their values.

type Order = {
id: String,
item: String,
}

Construct by naming every field; read with dot access; produce a changed copy with the spread form:

fn rename(o: Order, item: String) -> Order {
Order { ...o, item: item }
}

Records are immutable — the spread copies and overrides.

A variant may carry a payload or not:

type Status =
| Pending
| Shipped(tracking: String)
| Cancelled(reason: String)

Construct by naming a variant (Pending, Shipped("1Z…")); consume with match, which must cover every variant.

An opaque type is backed by another type but is not interchangeable with it:

type OrderId = opaque String

Inside the module that defines it, construct with OrderId.unsafe("ord-1") (or OrderId.of(...) for a checked Result). You cannot pass a plain String where an OrderId is expected, which is the point.

commons shop {
type OrderId = opaque String
type Status =
| Pending
| Shipped(tracking: String)
type Order = {
id: OrderId,
status: Status,
}
fn newOrder(id: OrderId) -> Order {
Order { id: id, status: Pending }
}
}