Values and types
Tuple values and types use parenthesized commas: (Int, String) and (x, y). A tuple always has at least two fields; trailing commas are accepted.
let pairpair: fn() -> (Int, String) = () -> (Int, String) => (77: Int, "ship""ship": String);
Unpacking
let unpacks nested tuples the same way.
Nested Tuples
let (countcount: Int, labellabel: String) = pairpair: fn() -> (Int, String)();
expect ✓countcount: Int == 77: Int;
expect ✓labellabel: String == "ship""ship": String;
let (firstfirst: Int, (secondsecond: Int, thirdthird: Int)) = (11: Int, (22: Int, 33: Int));
expect ✓firstfirst: Int == 11: Int;
expect ✓secondsecond: Int == 22: Int;
expect ✓thirdthird: Int == 33: Int;
let (prefixprefix: Int, ..) = (44: Int, 55: Int, 66: Int);
expect ✓prefixprefix: Int == 44: Int;Labels
~name: value labels a tuple field. Labels are semantic and stay attached to their physical positions, so changing their order changes the type. ~name puns ~name: name.
type Bounds = (min: Int, max: Int)
let boundsbounds: fn(min: Int, max: Int) -> (min: Int, max: Int) = (minmin: Int, maxmax: Int) -> Bounds => (~minmin: Int, ~maxmax: Int);
Labelled Returns
let resultresult: (min: Int, max: Int) = boundsbounds: fn(min: Int, max: Int) -> (min: Int, max: Int)(22: Int, 99: Int);
✓resultresult: (min: Int, max: Int): Bounds;
let (~minmin: Int, ~maxmax: Int) = resultresult: (min: Int, max: Int);
expect ✓(minmin: Int, maxmax: Int) == (22: Int, 99: Int);Labels may be mixed with positional fields. Construction order remains runtime order.
type Scan = (value: String, Int, after: Int)
let scanscan: fn(value: String, width: Int, after: Int) -> (value: String, Int, after: Int) = (valuevalue: String, widthwidth: Int, afterafter: Int) -> Scan => (~valuevalue: String, widthwidth: Int, ~afterafter: Int);
Mixed Labels
let resultresult: (value: String, Int, after: Int) = scanscan: fn(value: String, width: Int, after: Int) -> (value: String, Int, after: Int)("ship""ship": String, 44: Int, 88: Int);
let (~valuevalue: String, widthwidth: Int, ~afterafter: Int) = resultresult: (value: String, Int, after: Int);
expect ✓(valuevalue: String, widthwidth: Int, afterafter: Int) == ("ship""ship": String, 44: Int, 88: Int);Labelled patterns
A known tuple type lets patterns name fields in a different order or omit fields with ... These are compile-time label lookups; the runtime tuple remains positional.
Labelled Patterns
let resultresult: (min: Int, max: Int) = boundsbounds: fn(min: Int, max: Int) -> (min: Int, max: Int)(33: Int, 1111: Int);
let (~maxmax: Int, ~minmin: Int) = resultresult: (min: Int, max: Int);
expect ✓(minmin: Int, maxmax: Int) == (33: Int, 1111: Int);
let (~max: upperupper: Int, ..) = resultresult: (min: Int, max: Int);
expect ✓upperupper: Int == 1111: Int;Products
Product let patterns bind fields by name.
type User = {
username: String;
contact_email: String;
}
Product Fields
let useruser: Book.LanguageReference.Tuples.User = { username = "Teej""Teej": String; contact_email = "teej@example.com""teej@example.com": String; };
let { usernameusername: String; contact_emailcontact_email: String; } = useruser: Book.LanguageReference.Tuples.User;
expect ✓usernameusername: String == "Teej""Teej": String;
expect ✓contact_emailcontact_email: String == "teej@example.com""teej@example.com": String;