8.5. Tuples
A tuple is a way of grouping multiple values with potentially different
types into an aggregate data structure. Tuples are similar to
structs, except that a tuple’s fields are indexed instead
of named. Tuples are often used to return multiple values from a function or
procedure. Any storable type may be stored within
a tuple, including arrays of any rank (a matrix is the rank-2 case),
vector, string, struct, and other tuple types, nested to any
depth (subject to the acyclicity rule). Only
streams may not be stored in a tuple.
8.5.1. Declaration
A tuple value is declared with the keyword tuple followed by a
parentheses-surrounded, comma-separated list of types. The list must
contain at least two elements; a tuple type with fewer than two members is
ill-formed, and the compiler must emit a TypeError (see
Errors). As with any other type, a tuple variable
is mutable only when declared var (see Type Qualifiers).
For example:
var tuple(integer, real, integer[10]) t1;
tuple(character, real, character[256], real) t2;
Note that while each tuple declaration defines a new type, the tuple type
is not named explicitly. Rather, it has a type signature (T1, T2, ...),
where T1, T2 are the types of its members.
The number of fields in a tuple must be known at compile time.
This includes instances of type inference,
where a variable is declared without an explicit type signature using
var or const.
In this case, the variable must be initialized immediately with an
expression whose type is known at compile time.
8.5.2. Access
The elements in a tuple are accessed using dot notation. Dot notation can only
be applied to tuple variables: applying it to a non-variable – the result of
an expression, or a tuple literal – must emit a TypeError (see
Errors), exactly as for struct field access.
Dot notation means an identifier followed by a period and then a literal
integer.
Field indices start at one, not zero. Because a tuple index must be a
literal, an
index less than one or greater than the tuple’s number of fields is caught at
compile time, and the compiler must emit an IndexError (see
Errors). For example:
t1.1
t2.4
Tuple access can be used either to retrieve the element value for an expression or to assign a new value to the element.
y = x + t1.1; // Allowed
t1.1 = type-expr; // Allowed
8.5.3. Literals
A tuple literal is constructed by grouping values together between parentheses in a comma separated list. For example:
tuple(integer, character[5], integer[3]) my_tuple = (x, "hello", [1, 2, 3]);
var our_tuple = (x, "hello", [1, 2, 3]);
const your_tuple = (x, "hello", [1, 2, 3]);
tuple(integer, real, integer[10]) tuple_var = (1, 2.1, [i in 1..10 | i]);
8.5.4. Operations
The following operations are defined on tuple values. In all of the usage
examples tuple-expr means some expression yielding tuples with the same
type signature, while int_lit is an integer literal as defined in
Integer Literals and tuple-inst is the name of a
tuple instance as defined in Identifiers.
Class |
Operation |
Symbol |
Usage |
Access |
dot |
|
|
Comparison |
equals |
|
|
not equals |
|
|
Note that in the above table tuple-inst always refers to a variable for
Access. Accessing a literal could be replaced immediately with the value
inside the tuple literal; however, tuple-expr may refer to a literal in
comparison operations to enable shorthand like this:
if ((a, b) == (c, d)) { }
Comparisons are performed pairwise. Two tuples are equal when for every
expression pair, the equality operator returns true. Two tuples are unequal
when one or more expression pairs are unequal. Comparing two tuples of
different type signatures with no common implicit-cast target must emit a
TypeError (see Errors); two signatures that differ but share a
common implicit-cast target compare legally after a two-sided implicit cast
(for example (1.0, 2) == (2, 3.0) – see Tuple to Tuple).
This table describes how the comparisons are completed, where t1 and t2
are tuple yielding expressions including literals:
Operation |
Meaning |
|---|---|
|
|
|
|
Operator precedence and associativity are specified once, for all types, in the table of operator precedence.
8.5.5. Unpacking
Any tuple expression may be assigned (unpacked) into multiple
lvalues. If the
size of the tuple being unpacked does not match the number of lvalues being
assigned, the compiler must emit an AssignError (see Errors).
There is no partial unpacking of tuples.
var real a;
var real b;
a, b = (3.14, 1.5);
8.5.6. Type Casting and Implicit Casts
To see the types that tuple may be cast and/or implicitly cast to, see the sections on Type Casting and Implicit Casts, respectively.