Status: Accepted
Date: October 15, 2026
Answers: whether a lambda’s parameter may take apart the tuple it is
handed, and then a function’s too (issue #623)
Related:
ADR-356 D1, D2 (the tuple pattern of a let and a for head);
ADR-291 (tuples, _);
ADR-231 (what a walk hands its lambda);
Part I 2.1 (Taking a tuple apart), 5.3; Part III 13.5, C.3
May a parameter be a tuple pattern? A walk hands its lambda one item; where
the item is a pair, the lambda can only name the pair and take it apart in its
body. A let and a for head take a tuple apart where they bind
(ADR-356); a parameter is the one binding place that cannot.
From memory, not checked against a current manual.
| Language | A parameter that takes a tuple apart |
|---|---|
| Rust | fn f((a, b): (i64, i64)) and \|(a, b)\|, in functions and closures alike |
| Kotlin | { (a, b) -> } in a lambda; not in a function |
| Swift | { (a, b) in } in a closure, after SE-0110 brought the special case back |
| JavaScript | ([a, b]) => … and function f([a, b]) |
| Python | removed in Python 3 (PEP 3113): a signature is an object asked at run time, the pattern had no name (.0), annotations and keyword calls could not reach it, and it was seldom used |
Where it exists it is marked with brackets; no language takes a pair apart by the count of the parameters alone.
| Option | Why it lost, or won |
|---|---|
1 Refuse it; let (a, b) = pair in the body |
one more line per lambda over pairs, and the one binding place without a pattern |
| 2a A bracketed pattern in a lambda only | a form a lambda has and a function not; two rules where one does |
| 2b A bracketed pattern in a function and a lambda | chosen (D1-D5) |
3 fn(a, b) takes a pair apart by count |
the meaning would follow an inferred type: a changed item type turns a destructuring into an arity error or back (Swift’s SE-0110) |
fn dist((x, y): (f64, f64)) -> f64 { … }
pairs.iter().filter(fn((name, v)) { v > 1 })
The pattern is ADR-356 D1’s: a name, _, or a parenthesised list of two or
more patterns, nested as written; (p) is p. A function writes the type of
the whole parameter after it, as for any parameter; a lambda’s is the item the
place hands it. A variant is NK1259 (ADR-356 D2). An option, which a call
names, is never a pattern.
The function’s type, its count of arguments and every caller see one
parameter of the tuple’s type: fn((String, i64)) -> bool. The pattern binds
names inside the body and is seen nowhere else. fn(a, b) stays two arguments,
and a lambda with two names that is handed one tuple stays NK1101, whose help
writes the pattern: fn((name, ty)).
mut and keep belong to the whole parameter.Changed by ADR-views-3 D2, D4: mut and keep belong to the whole parameter; ref is not written.
(a, b): (String, i64) lends each part as a view of the argument’s part;
mut (a, b): … makes each part changeable, and a change reaches the caller as
for any mut parameter; keep (a, b): … keeps the whole argument.
The signature writes the pattern as the source does,
"((a, b): (ref String, ref String)) -> ref(a | b) String", and every other
key that names a parameter (keeps, returns, throws’ from(p), code,
maps, views, touches, sharing) names a part by its name, which is
unique in the function. A caller reads a part as the component of its argument
at that position. A message names such a parameter as it is written.
let.The parameter takes a name nobody can write, and the body begins by taking it
apart, with ADR-356’s lowering; a part of a borrowed tuple is read as the
for head reads it.
let, a for head, a lambda’s and a function’s parameter all bind
names to a value; one pattern for all four is one rule to learn. Brackets
mark it, so nothing is decided by a type the reader does not see.sync(f), from(f) and
arity what they are.left: ref(a) and job.run already are. A lambda that one day
has an entry of its own is named the same way.Rejected: 1, 2a and 3, above.
NK1101’s help for a lambda over a tuple writes fn((a, b)).NK1259 also refuses a variant in a parameter.