Scarlet Industries

Types

Enums, records, aliases and opaque types, and the exhaustive match that takes them apart.

Enums and records

Enums declare variants. Records declare fields. Both are type.

type Stat {
	files Int
	dirs Int
	bytes Int
}

fn totals_for(root String) Nil {
	totals = Stat(files: 0, dirs: 0, bytes: 0)
	next = Stat(..totals, dirs: totals.dirs + 1)
	Stat(files, dirs, bytes) = walk(root)
	println('${next.dirs} ${files} ${dirs} ${bytes}')
}

Construction uses labelled arguments. .. spreads an existing record into a new one. A record pattern in binding position destructures it. Type parameters are lowercase names, filled in fresh at every use site.

/** A generic enum: a persistent stack, recursive in its own type parameter. */
type Stack(a) {
	Empty
	Top(head a, rest Stack(a))
}

fn twice(f fn(a) a, x a) a {
	f(f(x))
}

Aliases

Aliases name existing types, generic and function types included.

type Path = String
type Bag(a) = Array(a)
type Validate = fn(Int) Result(Int, String)

Opaque types

pub opaque type exports a type and keeps its constructors private, so a value can only be built through the module's own functions. Wrap domain values this way: an amount that is its own opaque type cannot be confused with a bare number, and mixing two currencies becomes a type error.

Pattern matching

match is checked for exhaustiveness. A missing variant is a compile error. So is an unreachable arm.

type Event {
	Click(at Point, button Int)
	Key(code Int, shift Bool)
	Scroll(dy Int)
	Quit
}

pub fn main() {
	e = Key(code: 27, shift: False)

	label = match e {
		// A nested constructor pattern reaches into the payload's payload.
		Click(Point(0, 0), _) -> 'click at the origin'
		// A guard refines a pattern with a test the pattern cannot express.
		Click(Point(x, y), _) if x == y -> 'click on the diagonal at ${x}'
		// Fields can be matched by label, and `..` drops the ones not named.
		Click(at: p, ..) -> 'click at ${p.x},${p.y}'
		Key(27, _) -> 'escape'
		Key(code, True) -> 'shift + key ${code}'
		Key(code, False) -> 'key ${code}'
		Scroll(1) -> 'scroll nudge down'
		Scroll(-1) -> 'scroll nudge up'
		Scroll(dy) -> 'scroll ${dy}'
		Quit -> 'quit'
	}

Patterns also cover or-patterns (200 | 201 | 204), end-exclusive ranges (400..500), string literals ('GET' | 'HEAD'), tuples, arrays ([first, ..rest]), and binaries, covered under Binaries.