7. Optionals, unions, and match

Model absence with T? and none, alternatives with unions, and consume both with match.

After this lesson you can

- Declare optional fields and default them with ?? - Construct union variants and dispatch bare records by shape - Destructure values with match, ending in a _ or binding arm

Before you start: The @document and gathering

A T? field accepts a value or the literal none; the ?? operator supplies the default (bob.bio ?? "anonymous"), evaluating its right side only when needed. Use T? when the only states are present and absent — when absence carries information, reach for a union.

A union names its variants: construct one as Status::Failed { reason: "timeout" }, or — where the expected type is already a union — write a bare record and let the field shape pick the variant. match takes the value apart; record payloads destructure as Status::Failed { reason } => ..., and the arm list must end with a _ wildcard or a binding arm so every case is covered.

§ 1Exercise: Absence and alternatives

Model a status union and an optional bio; extract the failure reason with match and default the bio with ??.

wcl
union Status {
  Ok     { code: u32 }
  Failed { reason: utf8 }
}

let bob = { name: "Bob", bio: none }

@document
type Doc {
  status:  Status
  label:   utf8
  display: utf8
}

status  = Status::Failed { reason: "timeout" }
label   = match status {
  Status::Failed { reason } => reason,
  _                         => "ok",
}
display = bob.bio ?? "anonymous"

Expected result

wcl eval doc.wcl label prints "timeout" and wcl eval doc.wcl display prints "anonymous".

Hint

Drop the _ arm and the parser refuses the match outright: every match must end with a wildcard or binding arm.