idea for Native D Tuple Syntax, packing and unpacking
Quirin Schroll
qs.il.paperinik at gmail.com
Mon Jun 24 17:17:25 UTC 2024
On Sunday, 23 June 2024 at 04:10:35 UTC, Kindly Doright wrote:
> Idea for Native D Tuple Syntax, packing and unpacking (for
> simplicty of compiler internals and identifiable language
> construct)
> ```d
> // #() = #();
> #(a, b, c, d) = #(1, 'strval', [1,2,3], true);
>
> // myTuple = #(values); #(values) = myTuple;
> auto y = #(1, 'strval', [1,2,3], true);
> #(a, b, c, d) = y;
> ```
Makes sense so far.
> ```d
> // Optional unpack assignment
> #(a) = #(1, 'strval', [1,2,3], true); // a = 1
> ```
Hard no to this one, but can be fixed easily:
```d
#(a, ...) = #(1, 'strval', [1,2,3], true); // a = 1
```
> ```d
> #(, b, , d) = #(1, 'strval', [1,2,3], true); // b = 'strval, d
> = true
> ```
Making the absence of something meaningful is generally not wise.
I’d rather have `_` have special meaning in matching to mean
discard.
Years ago, I implemented `tie[]` using `opIndex` and
`opIndexAssign` which used `typeof(null)` as a discard and you
can use `enum typeof(null) _` to just use `_` for discarding. A
cut-down proof-of-concept implementation is below.
What my implementation can’t do because it would require
first-class language support is tuple-rest unpacking. It also
can’t declare variables. Borrowing your syntax:
```d
#(_, b, _, d) = #(1, "strval", [1,2,3], true); // b = 'strval, d
= true
```
> ```d
> // Embedded Tuple Syntax
> #(a, b, c, d, eee) = #(1, 'strval', [1,2,3], true, #(5,
> 'str2'));
> #(f, g) = eee; // f = 5, g = 'str2'
> ```
What about:
```d
#(a, b, c, d, #(e, f)) = #(1, 'strval', [1,2,3], true, #(5,
'str2'));
```
What about:
```d
#(a, b, cd..., e, f) = #(1, "strval", [1,2,3], true, 5, "str2");
#(c, d) = cd; // c == [1,2,3], d == true
```
If it were up to me, I’d use `[…]t` syntax for tuples and `[…]s`
for static arrays.
Unpacking would be:
```d
[x, y]t = rhs; // for `x` and `y` already defined
auto [x, y]t = rhs; // Declares `x` and `y`.
[x, auto y]t = rhs; // for `x` already defined; declares `y`.
// sets `x`, `y`, and `zs` where `zs` must be a type such that
`zs[0]`, …, `zs[n-1]` work, where `n` is `rhs.length - 2` if it
is already declared:
[x, y, zs...]t = rhs; // for `x`, `y`, and `zs` already defined
auto [x, y, zs...]t = rhs; // / Declares `x`, `y`, and `zs`.
[auto x, y, auto zs...]t = rhs; // for `y` already defined;
declares `x` and `zs`.
```
Static arrays would also work, but require/produce same-type
values, possibly using common-type inference.
---
```d
struct tie
{
import core.lifetime;
import std.typecons : tuple;
static opIndex(Ts...)(auto ref Ts args) =>
tuple(forward!args);
alias opIndexAssign = opIndexOpAssign!"";
template opIndexOpAssign(string op)
{
static void opIndexOpAssign(R, Ts...)(R rhs, auto ref Ts
lhs)
{
static foreach (i; 0 .. Ts.length)
{
static if (!is(Ts[i] : const(typeof(null))))
{
static assert(__traits(isRef, lhs[i]));
mixin("lhs[i] ", op,"= rhs[i];");
}
}
}
}
}
void main()
{
int x;
double y;
tie[x, y] = tie[2, 3.14];
assert(x == 2);
assert(y == 3.14);
tie[x, y] += tie[3, 2.71];
assert(x == 5);
assert(y == 3.14 + 2.71);
int z;
enum _ = null;
tie[z, _] = tie[x, y];
assert(z == x);
}
```
More information about the dip.ideas
mailing list