Structs & Enums

User-defined compound types with named fields, and type-safe enumerations with named variants.

Structs

Structs are user-defined compound types declared with the struct keyword. Each struct has a fixed set of named fields.

Declaration

struct Point { x; y }
struct Color { r; g; b; a }

Positional initialization

Supply values in the order the fields are declared. All fields must be provided.

var p = Point(10, 20)
p.x    // 10
p.y    // 20

var c = Color(255, 128, 64, 255)

Named initialization

Use .field = value syntax for explicit field assignment. Fields can appear in any order. Unset fields default to null.

named initialization
// Any order
var p1 = Point{.y = 100, .x = 50}
p1.x    // 50
p1.y    // 100

// Partial — unset fields are null
var p2 = Point{.x = 7}
p2.x    // 7
p2.y    // null

Field access and mutation

var p = Point(1, 2)

// Read
var sum = p.x + p.y    // 3

// Mutate
p.x = 99
p.y = p.y * 2          // 4

// Chained assignment
p.x = p.y = 42         // both fields are now 42

Structs in collections

Structs can be stored in lists and maps, and their fields accessed through the collection.

structs in collections
// In a list
var points = [Point(1, 2), Point(3, 4)]
points[0].x      // 1
points[1].y      // 4
points[0].x = 99 // mutation through list access

// In a map
var named = { origin: Point(0, 0), unit: Point(1, 1) }
named["origin"].x   // 0
named["unit"].x = 10

Nested structs

Struct fields can hold other structs, creating nested structures.

struct Rect { topLeft; bottomRight }

var rect = Rect(Point(0, 0), Point(100, 50))
rect.topLeft.x         // 0
rect.bottomRight.y     // 50
rect.topLeft.x = 10    // mutation through nesting

Structs with memory qualifiers

Structs interact with ref, val, slot, and clone just like any other type. See Memory Semantics for details.

func nudge(ref p) {
    p.x = p.x + 1   // modifies caller's struct
}

func safeCopy(val p) {
    p.x = 999       // local copy — caller unchanged
    return p
}

var origin = Point(0, 0)
nudge(origin)          // origin.x is now 1

var copy = safeCopy(origin)
origin.x               // still 1
copy.x                 // 999

Spread with structs

Use the spread operator to copy fields from an existing struct and selectively override them.

var p1 = Point{.x = 1, .y = 2}
var p2 = Point{...p1, .y = 10}
p2.x    // 1  — copied from p1
p2.y    // 10 — overridden

Schema scope hoisting

If you define a struct inside a function but return an instance of it, the schema is automatically pulled into the caller’s scope. This lets you keep the definition private while making the result usable anywhere.

schema hoisting on return
func makePoint(x, y) {
    struct Point { x; y }    // definition is scoped to this function
    return Point(x, y)       // returning an instance hoists the schema
}

var p = makePoint(3, 4)
p.x    // 3 — the instance is fully usable
p.y    // 4
Why this matters: The struct definition and initializer remain scoped inside the function — callers cannot construct new instances directly. Only the returned instance (and its schema metadata) escapes. This is a powerful pattern for encapsulating construction logic while exposing usable values.

Enums

Enums are user-defined enumeration types declared with the enum keyword. Each enum has a fixed set of named variants.

Declaration and access

basic enums
enum Color { RED, GREEN, BLUE }
enum Status { ACTIVE, INACTIVE }

var c = Color.RED
var s = Status.ACTIVE

Enums can have any number of variants, including just one:

enum Days { MON, TUE, WED, THU, FRI, SAT, SUN }
enum Single { ONLY }

Comparison and equality

Enum values of the same type can be compared with == and !=.

var a = Color.RED
var b = Color.RED
var c = Color.BLUE

a == b    // true
a != c    // true
a == c    // false

// Assignment preserves equality
var d = a
a == d    // true

Type safety

Different enum types are not comparable. Comparing variants from different enums is a type error.

type safety
enum Animal { DOG, CAT }
enum Vehicle { CAR, BIKE }

var a = Animal.DOG
var v = Vehicle.CAR
// a == v → type error! Different enum types

Enums in conditionals

Use enum comparisons to drive control flow.

enum-driven logic
enum Direction { UP, DOWN, LEFT, RIGHT }

func describe(dir) {
    if (dir == Direction.UP) return "Going up"
    if (dir == Direction.DOWN) return "Going down"
    if (dir == Direction.LEFT) return "Going left"
    return "Going right"
}

describe(Direction.UP)    // "Going up"

Enums in collections

Enum values can be stored in lists, maps, and struct fields.

enum Priority { LOW, MEDIUM, HIGH }

// In a list
var queue = [Priority.HIGH, Priority.LOW, Priority.MEDIUM]
queue[0] == Priority.HIGH   // true

// In a map
var config = { level: Priority.MEDIUM }
config.level == Priority.MEDIUM   // true

// In a struct
struct Task { name; priority }
var task = Task("Deploy", Priority.HIGH)
task.priority == Priority.HIGH    // true

Enums with memory qualifiers

Enum values work with ref, val, slot, and clone like any other value type.

func promote(ref p) {
    if (p == Priority.LOW) p = Priority.MEDIUM
    else if (p == Priority.MEDIUM) p = Priority.HIGH
}

var level = Priority.LOW
promote(level)
level == Priority.MEDIUM   // true — ref modified caller

Scoping

Enums follow normal scoping rules. An enum declared in an inner scope can shadow one in an outer scope.

enum Mode { ON, OFF }
var m = Mode.ON

{
    enum Mode { FAST, SLOW }   // shadows outer Mode
    var inner = Mode.FAST
    // Mode.ON is not accessible here
}

// Outer Mode is restored
m == Mode.ON   // true

Like structs, if an enum is defined inside a function but an instance is returned, the schema is automatically hoisted to the caller’s scope.

enum schema hoisting
func getStatus() {
    enum Status { OK, ERR }
    return Status.OK           // schema hoisted on return
}

var s = getStatus()            // s holds Status.OK