Skip to main content

11  Records, Tuples, Bitfields & Enums

Edva provides records for product types (structs, tuples, SIMD vectors, packed headers, and bitfields) and enums for nominal tagged unions with \(\ge 2\) named variants.

11.1 Product types: records, tuples, #packed, and bitfields

11.1.1 Record declaration forms

Form Syntax Layout & Semantics Diagnostic
Named record #type Point (x: Int, y: Int = 0) C-ABI aligned struct; fields may declare default values (= expr) E2015: missing comma on same line
Multiline record #type User
id: Int
name: String
Indented or parenthesized multiline fields; newline separates aligned entries —
Positional tuple #type Pair (Int, String)
p = (10, "ok")
Accessed via .0, .1, … —
Homogeneous / SIMD tuple #type Vec4 (Float * 4)
v = 0.0 {4}
Contiguous inline elements; supports v(i) indexing, +/-/*// arithmetic, and % SIMD intrinsics —
Packed record #type Hdr #packed (a: u8, b: u32) Alignment 1, zero interior or tail padding —
Explicitly aligned record #type Block #align 64 (data: u8 * 64) Power-of-two byte alignment N; #size tail-padded to multiple of N (combinable with #packed) —
Bitfield record #type Tcp #packed u16 (syn: <>, ack: <>, win: 14) Packs 1-bit <> flags (<-> default) and integer bit widths into u8, u16, u32, or u64 E2055: bit sum exceeds backing width

11.1.2 Record construction, access, and definite initialization

Operation Syntax Semantics Diagnostic
Positional construction Point(10, 20) / (10, 20) Initializes fields in declaration order E3084: field type/count mismatch
Named construction Point(y = 20, x = 10) Initializes by field name; omitted fields with defaults use their declared default value —
Zero/default value Point.zero Initializes all fields to 0 / <-> / "" or their declared = default expression —
Forward declaration p: Point Allocates uninitialized record storage; every field must be written before being read E3130: reading uninitialized field
Field read / write p.x, p.0, p.x = 5 Reads or mutates named, positional, or bitfield slot in place —
Field-accessor section (.x), (.0), (.a.b) 1-argument projection function r => r.x E2064: bare (.)
Structural equality p1 == p2, p1 != p2 Compares all fields recursively E3084: if record contains fn field
#use io

#type Point (x: Int = 0, y: Int = 0)
#type Flags #packed u8 (fin: <>, syn: <>, rest: 6)

p: Point
p.x = 10
p.y = 20

f' = Flags(fin = <->, syn = <+>, rest = 7)

io::out $ (p.x + p.y, f'.syn, f'.rest, #size Flags)
#type Point (x: Int, y: Int)
p: Point
p.x = 100
bad = p.y

11.2 Sum types: nominal Enum (< V1, V2(T) >)

Enums model nominal tagged unions of two or more uppercase variants, represented at runtime as a 16-byte { i64 tag, ptr payload } pair (#size Enum == 16). Because payloads are arena pointers, recursive enums (#type Expr < Lit(Int), Add(Expr, Expr) >) have fixed 16-byte size without explicit box types.

11.2.1 Enum declaration and construction rules

Rule / Operation Syntax Specification Diagnostic
Declaration #type Shape < Circle(Float), Rect(Float, Float), Dot > Requires \(\ge 2\) unique uppercase variant labels separated by commas or aligned newlines E2070: \(< 2\) variants
E2012: lowercase label
E2074: duplicate label
Unit variant construction Shape(Dot), Dot, Shape::Dot Bare Dot is bound in scope if unambiguous; use Shape::Dot when two enums share a label E3105: ambiguous unqualified label
E3098: payload given to unit variant
Payload construction Shape(Circle, 5.0)
Shape(Rect, (3.0, 4.0))
Type name called with variant label first, followed by payload argument(s) E3096: unknown variant
E3097: missing payload
E3099: wrong payload type
Structural equality a == b, a != b Compares variant tags first, then payloads recursively (<, > ordering rejected) E3084 if fn payload
E3127 on < / >

11.2.2 Enum pattern matching and reflection

Pattern / Operation Syntax Semantics Diagnostic
Unit / ignore-payload guard [Dot] / [Dot, Circle] Matches tag(s); ignores any payload ([Circle _] is forbidden; write [Circle]) E2109: redundant _ in [V _]
Payload binder guard [Circle r] Matches Circle and binds payload to r (and implicit _ in branch body) —
Deep nested pattern [Add (Lit a, Neg (Lit b))] Recursively destructures nested enum and tuple/record payloads in a single guard —
Exhaustiveness All variants covered or trailing | Required when choice result is used as a value E3102: uncovered variants
Compile-time count & label ?T, T.(i) (with #! T: Type) ?T yields variant count; T.(i) yields i-th variant name (String) E3170: non-constant i
#use io

#type Expr < Lit(Int), Neg(Expr), Add(Expr, Expr) >

eval: Expr => Int
eval = e =>
   e
      [Lit n]              n
      [Neg (Lit n)]        0 - n
      [Neg x]              0 - eval(x)
      [Add (Lit 0, rhs)]   eval(rhs)
      [Add (lhs, rhs)]     eval(lhs) + eval(rhs)
      |                    0

e = Expr(Add, (Expr(Lit, 10), Expr(Neg, Expr(Lit, 3))))
io::out $ eval(e)   // 7