Unpacking syntax
Timon Gehr
timon.gehr at gmx.ch
Thu Sep 5 22:08:58 UTC 2024
These are my ideas for how to support unpacking any tuple-like type into
multiple components.
Synopsis:
```d
import std.typecons: t=tuple, T=Tuple;
void main(){
// unpack declarations
auto (a, (b, c)) = t(1, t(2, "3"));
assert(t(a, b, c) == t(1, 2, "3"));
import std.stdio, std.string, std.conv;
auto (u, v) = readln().strip.split.to!(T!(int,int));
// can unpack in foreach
foreach(i, (x, y); [t(1, 2), t(3, 4)]) {
assert(x==2*i+1 && y==2*i+2);
}
// works with storage classes
auto arr = [t(1, 2), t(3, 4)];
foreach((ref x, y); arr) {
x = 2*y;
}
foreach(const (x, y); arr) {
static assert(is(typeof(x) == const(int)));
static assert(is(typeof(y) == const(int)));
assert(x == 2*y);
}
// works with opApply
static struct Iota2d{
int start,end;
int opApply(scope int delegate(T!(int,int)) dg){
foreach(i; start .. end) {
foreach(j; start ..end) {
if(auto r = dg(t(i,j)))
return r;
}
}
return 0;
}
}
bool[4][4] visited;
foreach((x, y); Iota2d(0,4)){
visited[x][y] = true;
}
import std.algorithm;
assert(visited[].all!((ref x)=>x[].all));
// works with ranges
import std.range;
foreach(i, (j, k); enumerate(arr)) {
writeln(i," ",j," ",k); // "0 4 2\n1 8 4\n"
}
// can unpack in lambda parameter list
[t(1, 2), t(2, 3)].map!( ((a, b)) => a+b ).each!writeln; // "3\n5\n"
// works with storage classes
arr.each!( ((ref x, y)){ x = 3*y; });
assert(arr.all!( (const (x, y)) => x == 3*y));
}
```
The code above works with my implementation, which can be found at:
https://github.com/tgehr/dmd/tree/unpacking
An unpacking declaration works if the right-hand side is an expression
sequence or has `alias this` to an expression sequence. The number of
elements has to match exactly.
Each variable without explicitly declared type that is unpacked to needs
to have at least one storage class.
```d
auto (a, b) = t(1, 2); // ok
(auto a, auto b) = t(1, 2); // ok
(a, auto b) = t(1, 2); // error
(auto a, b) = t(1, 2); // error
```
This is less confusing and would allow this syntax to be used for mixed
variable declaration and reassignment in the future.
Types can be declared explicitly, or inferred, independently for each
variable:
```d
(int a, (string b, auto c)) = t(1, t("2", 3.0f));
```
Note that it is _not_ possible to declare a type for the whole unpacking
explicitly:
```d
Tuple!(int, int) (a, b) = t(1, 2); // error
```
Storage classes can be applied to all unpacked variables independently.
```d
(auto a, const b, immutable c) = t(1, 2, 3);
```
Unpacking works with all variants of the `foreach` statement. (See
synopsis for some examples, `foreach((x, y); a .. b)` can work too if
`a` and `b` happen to be tuple-like types.) Here, storage classes are
not required, consistent with how `foreach` works without unpacking.
Unpacking works in the parameter list of a function literal:
```d
int function(Tuple!(int, int)) f = ((x, y)) => x + y;
auto summands = t(1, 2);
writeln(f(summands))); // "3\n"
```
Unpacking does not work in the parameter list of a function that is not
a literal. The reason for this is that there is no canonical type for
the corresponding parameter:
```d
auto foo((int a, int b), int c){} // error
```
This restriction can be lifted at some point if we add built-in tuple types.
In `foreach` statements and in function literal parameters, the `ref`
storage class can be applied to individual variables within an
unpacking. They will cause the entire structure to be passed by `ref`,
but non-`ref` unpacked variables will be initialized by value. (See
synopsis for some examples.)
The `lazy` storage class is not supported for unpacked parameters, as
that does not seem to make sense.
Limitations:
- The `auto ref` storage class is not currently supported on unpacking
declarations.
- Applying the `out` storage class to individual variables within an
unpacking is not currently supported.
I think this is a decent minimum viable product in terms of unpacking.
Future work:
- Move semantics for unpacking. (Currently it will do too many copies.)
- Do we want some way to define a manual unpacking without `alias this`?
- Do we want to be able to directly unpack static arrays and array
slices of the correct length?
- `auto ref` support.
- `out` parameters in unpackings, e.g. `((in x, out y)){ y=x; }`
- Do we want a way to partially unpack? E.g., `auto (x, y, ...) = t(1,
2, 3, 4);`
- Wildcards. E.g., `auto (_, x) = t;`
- General tuple syntax. (WIP at:
https://github.com/tgehr/dmd/tree/tuple-syntax )
More information about the dip.ideas
mailing list