7.1.11. Tuples and Variants
This tutorial covers tuples (anonymous and named), the => tuple construction
operator, tuple destructuring, variants (tagged unions), and type-safe variant
access with is, as, and ?as.
7.1.11.1. Tuples
A tuple groups values of different types into a single value. Access fields by index:
var pair = (42, "hello")
print("{pair._0}, {pair._1}\n") // 42, hello
Use tuple() for explicit construction:
let t = tuple(1, 2.0, "three")
7.1.11.2. The => operator
The => operator creates a 2-element tuple from its left and right operands.
It works in any expression context, not just table literals:
let kv = "age" => 25 // tuple<string; int>
print("{kv._0}: {kv._1}\n") // age: 25
This is useful for building arrays of key-value pairs:
var entries <- ["one" => 1, "two" => 2]
for (entry in entries) {
print("{entry._0} => {entry._1}\n")
}
Table literals also use => — each key => value pair forms a tuple that
is inserted into the table.
7.1.11.3. Named tuples
Give a tuple named fields for readability:
tuple Point2D {
x : float
y : float
}
let p = Point2D(x=3.0, y=4.0)
print("{p.x}, {p.y}\n")
7.1.11.4. Shorthand: unnamed → named promotion
When the target type is a named tuple and every element of a positional literal is a bare variable reference whose name matches the field name in order, the compiler promotes the literal to the named tuple — no need to repeat the field names:
let eid = 7
let distSq = 2.5
var hit : tuple<eid:int; distSq:float> = (eid, distSq) // ok, promoted
var hits : array<tuple<eid:int; distSq:float>>
hits |> push((eid, distSq)) // ok, promoted
It also fires in return position when the function’s declared result is a named tuple:
def make_hit(eid : int; distSq : float) : tuple<eid:int; distSq:float> {
return (eid, distSq) // ok, promoted
}
Promotion is fallback-only: if an unnamed-tuple overload already matches, it
wins. Use the explicit (name = value) literal to force the named overload:
def overload_pick(x : tuple<int; float>) { return 1 }
def overload_pick(x : tuple<x:int; y:float>) { return 2 }
overload_pick((x, y)) // 1 — unnamed overload wins
overload_pick((x=x, y=y)) // 2 — explicit named literal
A name mismatch fails compilation (no silent fallback to unnamed). Mixed
expressions like (a, a + 1) are not bare variable references and never
promote.
7.1.11.5. Destructuring
Unpack a tuple into individual variables:
let (a, b, c) = tuple(10, 20.0, "thirty")
This also works in for loops:
var pairs <- [tuple(1, "one"), tuple(2, "two"), tuple(3, "three")]
for ((num, name) in pairs) {
print("{num}: {name}\n")
}
7.1.11.6. Variants
A variant holds exactly one of several typed alternatives — a tagged union:
variant Value {
i : int
f : float
s : string
}
let v : Value = Value(i = 42)
7.1.11.7. Checking the active case
Use is to test which alternative is active:
if (v is i) {
print("it's an int\n")
}
Use as to extract the value (panics if wrong):
let n = v as i // 42
Use ?as for safe access with a fallback:
let maybe_f = v ?as f ?? 0.0
The variant_index() function returns the zero-based index of the active case.
See also
Tuples, Variants in the language reference.
Full source: tutorials/language/11_tuples_and_variants.das
Next tutorial: Function Pointers