A tuple is a fixed-size heterogeneous product with positional fields.
entry: (string, int, bool) = ("port", 8080, true)The type and value forms both use parentheses and commas.
Access
Use a zero-based numeric member.
name := entry.0
port := entry.1
enabled := entry.2The index is checked at compile time. A value outside the tuple’s arity is an error, and positional access on a non-tuple is rejected.
The index is syntax, not an arbitrary runtime expression: entry.1 selects a
known field, while entry[index] is not dynamic tuple indexing. Use a
homogeneous list or array when the position is chosen at runtime.
Use a struct or record when field names communicate meaning better than positions.
Destructuring
A tuple pattern binds its elements.
(host, port) := ("localhost", 8080)In binding and iteration positions, the outer parentheses may be omitted when the commas are unambiguous.
host, port := ("localhost", 8080)
for key, value in entries {
store(key, value)
}Nested tuple patterns can combine with structs, variants, and wildcards.
(Ok(value), _) := pair else {
return None
}The pattern arity must agree with the tuple type. _ ignores one position; it
does not absorb an arbitrary number of tuple elements.
Returns
Tuples provide multiple return components without a dedicated multiple-return calling convention.
fn divmod(value: int, divisor: int): (int, int) {
return (value / divisor, value % divisor)
}
quotient, remainder := divmod(17, 5)The returned value is one tuple whose element types are part of the function signature.
Atoll also accepts a parenthesis-free tuple in a return or binding expression where the comma belongs to that expression.
fn pair(): (int, string) {
return 1, "one"
}Use parentheses when surrounding calls or operators could make comma ownership unclear.
A function call’s argument commas remain call syntax rather than constructing
one tuple argument. Pass (a, b) explicitly when a function expects a single
pair.
Unit
() is the unit value. It represents the result of an expression with no
useful payload.
unit := ()Source function signatures normally spell no-result as void. Internal
prelude fixtures may expose Unit.
Types
Tuple identity is structural and ordered.
(int, string)This differs from (string, int), and both differ from a named struct even if
the struct contains corresponding fields.
Tuples can appear inside generics, options, results, arrays, and anonymous unions.
lookup: Map[string, (int, bool)]
maybe_pair: (string, int)?Expected tuple types can constrain their elements, including lifting a bare
value into an optional element. The current checker does not apply every
per-element coercion, however: write (1.0, "one") rather than relying on 1
to widen inside an expected (float, string?). Tuples of different arity never
unify.
Semantics
Tuple elements use their normal value representations. All-scalar tuples copy their lanes. A tuple containing managed data retains and releases those fields according to the ordinary value rules.
Tuple equality requires compatible element equality behavior. The tuple itself is not an object with an independent hidden identity.
The empty tuple () is unit. Parentheses around one ordinary expression are
grouping unless tuple punctuation makes a tuple shape explicit; do not use a
one-element tuple merely to simulate a nominal wrapper.
Choosing tuples
Use a tuple for:
- a small local product;
- a conventional paired result;
- destructuring in a loop;
- an implementation detail whose positions are obvious.
Use a named struct or record when the product crosses a public boundary, grows beyond a few fields, or needs field names and behavior.