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)
// #() = #();
#(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.
>// Optional unpack assignment
#(a) = #(1, 'strval', [1,2,3], true); // a = 1
Hard no to this one, but can be fixed easily:
#(a, ...) = #(1, 'strval', [1,2,3], true); // a = 1
> #(, 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:
#(_, b, _, d) = #(1, "strval", [1,2,3], true); // b = 'strval, d = true
> // 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:
#(a, b, c, d, #(e, f)) = #(1, 'strval', [1,2,3], true, #(5, 'str2'));
What about:
#(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:
[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.
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);
}