Skip to content

Tuples

A tuple groups a fixed number of values of possibly-different types into one. Unlike a record, a tuple’s elements are positional: you reach them by their position, not by a field name.

Tuples are written with the #(...) syntax. The hash distinguishes them from parenthesized expressions.

let point = #(1.0, 2.0) // #(Double, Double)
let tagged = #("active", 42) // #(String, Int)
let nested = #(#(0, 0), #(1, 1)) // tuple of tuples

There is no one-element tuple. #(x) is not a valid syntax; a single value is just the value.

Tuples destructure in let, in for..in heads, in match arms, and in function parameters whose types are tuples.

let #(x, y) = point
debug "x=${x}, y=${y}"
// Ignore an element with _
let #(_, y) = point
// In a for loop
for #(index, value) in entries {
debug "${index}: ${value}"
}
// In match
fn describe(p: #(Int, Int)): String {
match p {
#(0, 0) => "origin",
#(_, 0) => "on x-axis",
#(0, _) => "on y-axis",
#(x, y) => "point (${x}, ${y})",
}
}
  • Returning two values. (name, age), (line, column), (ok, reason).
  • Map entries. Map.of([#("Alice", 30), #("Bob", 25)]) uses a list of 2-tuples as key-value pairs.
  • Enumerated iteration. list.enumerate() returns List<#(Int, T)>.

If the elements are meaningfully named, prefer a type with named fields instead:

// Prefer a named record when the fields have meaning
type Location {
Location(line: Int, column: Int)
}
// Tuple is fine when the role of each slot is obvious from context
fn minMax(xs: List<Int>): Option<#(Int, Int)> { ... }

Tuples are compared by structural equality (element-wise): two tuples of the same type with equal elements are equal.

let a = #(1, "x")
let b = #(1, "x")
debug a == b // true

Tuples emit as a Vertex-internal record in the generated Apex. You rarely need to reason about this; they behave as ordinary values at both the Vertex and Apex levels.