April 23, 2020
On Thursday, 23 April 2020 at 20:08:51 UTC, Stefan Koch wrote:
>
> k tuples with n elements can only expand to 1 tuple with k*n elements.
> They can't get more.

Acutely let me be more specific.

given TTup = T!(int, long, double, float)
the expression cast(TTup)0... yields
tuple(0, 0L, 0.0, 0.0f)
given VTup = T!(1, 2, 3, 4)
you can write the expression cast(TTup)VTup...
which yields tuple(1, 2L, 3.0, 4.0f)
should the length's of the tuple in and subexpression using multiple tuples differ, that's an error.
April 23, 2020
On 4/23/20 4:08 PM, Stefan Koch wrote:
> On Thursday, 23 April 2020 at 19:27:31 UTC, Steven Schveighoffer wrote:
>> On 4/23/20 3:02 PM, Stefan Koch wrote:
>>> On Thursday, 23 April 2020 at 18:57:04 UTC, Steven Schveighoffer wrote:
>>>> [...]
>>>
>>> It's the latter.
>>> you cannot re-expand.
>>
>> Those 2 are contradictory statements.
>>
>> If it's the latter, then tuple1... is expanded to a tuple that is then used in the outer expansion.
>>
> 
> k tuples with n elements can only expand to 1 tuple with k*n elements.
> They can't get more.

You have lost me.

Can you go through a blow-by-blow of how this nested expansion happens?

alias tuple1 = AliasSeq!(1, 2, 3);
alias tuple2 = AliasSeq!(4, 5, 6)

AliasSeq!(tuple1..., tuple2)...

-Steve
April 23, 2020
On Thursday, 23 April 2020 at 20:23:03 UTC, Steven Schveighoffer wrote:
> On 4/23/20 4:08 PM, Stefan Koch wrote:
>> On Thursday, 23 April 2020 at 19:27:31 UTC, Steven Schveighoffer wrote:
>>> On 4/23/20 3:02 PM, Stefan Koch wrote:
>>>> On Thursday, 23 April 2020 at 18:57:04 UTC, Steven Schveighoffer wrote:
>>>>> [...]
>>>>
>>>> It's the latter.
>>>> you cannot re-expand.
>>>
>>> Those 2 are contradictory statements.
>>>
>>> If it's the latter, then tuple1... is expanded to a tuple that is then used in the outer expansion.
>>>
>> 
>> k tuples with n elements can only expand to 1 tuple with k*n elements.
>> They can't get more.
>
> You have lost me.
>
> Can you go through a blow-by-blow of how this nested expansion happens?
>
> alias tuple1 = AliasSeq!(1, 2, 3);
> alias tuple2 = AliasSeq!(4, 5, 6)
>
> AliasSeq!(tuple1..., tuple2)...
>
> -Steve

This nested expression does not parse.
And I don't think it should.
Because we cannot nest tuples in tuples.
April 23, 2020
On 4/23/20 4:34 PM, Stefan Koch wrote:
> On Thursday, 23 April 2020 at 20:23:03 UTC, Steven Schveighoffer wrote:
>> On 4/23/20 4:08 PM, Stefan Koch wrote:
>>> On Thursday, 23 April 2020 at 19:27:31 UTC, Steven Schveighoffer wrote:
>> You have lost me.
>>
>> Can you go through a blow-by-blow of how this nested expansion happens?
>>
>> alias tuple1 = AliasSeq!(1, 2, 3);
>> alias tuple2 = AliasSeq!(4, 5, 6)
>>
>> AliasSeq!(tuple1..., tuple2)...
>>
> 
> This nested expression does not parse.
> And I don't think it should.
> Because we cannot nest tuples in tuples.

OK, so the answer is, you can only have one ... per expression?

-Steve
April 23, 2020
On 4/22/2020 5:04 AM, Manu wrote:
> [...]

Well done, Manu! This looks like a very good idea. I'm optimistic about it. Thank you for pushing this forward!
April 24, 2020
On Fri, Apr 24, 2020 at 2:30 AM Steven Schveighoffer via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 4/23/20 9:53 AM, Manu wrote:
>
> >
> > You can do this by expanding tuples with the appropriate indices:
> >    fun(Xs[CrossIndexX], Ys[CrossIndexY])...
> > Where CrossIndexX is (0, 0, 1, 1) and CrossIndexY is (0, 1, 0, 1).
>
> I don't think this works, aren't Xs and Ys tuples also?
>
> I think what you need is to expand the Xs and Ys into further tuples:
>
> alias XParams = Xs[CrossIndexX]...;
> alias YParams = Ys[CrossIndexY]...;
> fun(XParams, YParams)...;
>
> Which would work I think.
>
> this means you need 4 pre-expression declarations! Two for the X and Y expansions, and two for the indexes.
>
> -Steve
>

Oh yeah, it was too late at night. Your solution looks right.


On a slightly unrelated note; there's a really interesting idea up-thread
about making nested `...` not be expanded by the outer `...`
For instance:

  template T(A, B...) { ... }
  T!(Tup1, Tup2...)...

What we're saying here is, Tup2 gets the identity expansion, but that result is NOT expanded with the outer; so the expansion above is:

  T!(Tup1, Tup2...)...  ->  ( T!(Tup1[0], Tup2...), T!(Tup1[1], Tup2...),
...,  T!(Tup1[$-1], Tup2...) )

This is opposed to currently where we recurse through nested expansions, effectively:

  T!(Tup1, Tup2)...  ->  ( T!(Tup1[0], Tup2[0]), T!(Tup1[1], Tup2[1]),
...,  T!(Tup1[$-1], Tup2[$-1]) )

So, despite Tup2's expansion is the identity expansion, this syntax allows
Tup2 to be passed to T!()'s variadic args as the tuple it is, rather than
expanded like Tup.
Using `...` in nested context this way gives articulate control over cases
where multiple Tuples in the tree should or shouldn't be expanded by `...`

I think this is also the natural rule; it follows from the claim that expansion is performed pre-semantic evaluation; nested `...` is only a tuple AFTER evaluation, so it seems natural that it should not be expanded by the outer expansion.


April 24, 2020
On Fri, Apr 24, 2020 at 5:05 AM Stefan Koch via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Thursday, 23 April 2020 at 18:57:04 UTC, Steven Schveighoffer wrote:
> > On 4/23/20 2:18 PM, Stefan Koch wrote:
> >> [...]
> >
> > The question is whether the expanded inner expression is considered a tuple for expansion later or not.
> >
> > alias tuple1 = AliasSeq!(1, 2, 3);
> > alias tuple2 = AliasSeq!(4, 5, 6);
> > pragma(msg, [AliasSeq!(tuple1..., tuple2)...]);
> >
> > If it expands tuple1 first, then expands tuple 2 without re-expanding the result of tuple1..., then it should be:
> >
> > [1, 2, 3, 4, 1, 2, 3, 5, 1, 2, 3, 6]
> >
> > If it expands both tuple1 and tuple2 together, it should be:
> >
> > [1, 4, 2, 5, 3, 6]
> >
> > -Steve
>
> It's the latter.
> you cannot re-expand.
>

I think the former is correct though. And that's a trivial change.

Our existing code will evaluate the latter, but that only happens because I put a special-case for doing that... and I think that special case is wrong. Natural semantics with no special handling is preferred. It allows much more articulate control, and creates more opportunities.


April 24, 2020
On Fri, Apr 24, 2020 at 8:05 AM Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 4/22/2020 5:04 AM, Manu wrote:
> > [...]
>
> Well done, Manu! This looks like a very good idea. I'm optimistic about
> it.
> Thank you for pushing this forward!
>

🎉🎉🎉

Huzzah! The suspense is lifted!


April 24, 2020
On Thursday, 23 April 2020 at 20:34:20 UTC, Stefan Koch wrote:
> Because we cannot nest tuples in tuples.

Not yet anyway.
April 23, 2020
On 4/22/2020 5:04 AM, Manu wrote:
> [...]

Ok, I've had a chance to think about it. It's a scathingly brilliant idea!

But (there's always a but!) something stuck out at me. Consider arrays:


void test()
{
    auto a = [1, 2, 3];
    int[3] b = a[]*a[]; // b[0] = a[0]*a[0]; b[1] = a[1]*a[1]; b[2] = a[2]*a[2];
    int[3] c = a[]*2; // c[0] = a[0]*2; c[1] = a[1]*2; c[2] = a[2]*2;
}

These look familiar! D tuples already use array syntax - they can be indexed and sliced. Instead of the ... syntax, just use array syntax!

The examples from the DIP:

=====================================
--- DIP
(Tup*10)...  -->  ( Tup[0]*10, Tup[1]*10, ... , Tup[$-1]*10 )

--- Array syntax
Tup*10

====================================
--- DIP
alias Tup = AliasSeq!(1, 2, 3);
int[] myArr;
assert([ myArr[Tup + 1]... ] == [ myArr[Tup[0] + 1], myArr[Tup[1] + 1], myArr[Tup[2] + 1] ]);

--- Array
alias Tup = AliasSeq!(1, 2, 3);
int[] myArr;
assert([ myArr[Tup + 1] ] == [ myArr[Tup[0] + 1], myArr[Tup[1] + 1], myArr[Tup[2] + 1] ]);

===================================
---DIP
alias Values = AliasSeq!(1, 2, 3);
alias Types = AliasSeq!(int, short, float);
pragma(msg, cast(Types)Values...);

---Array
alias Values = AliasSeq!(1, 2, 3);
alias Types = AliasSeq!(int, short, float);
pragma(msg, cast(Types)Values);

=================================
---DIP
alias OnlyTwo = AliasSeq!(10, 20);
pragma(msg, (Values + OnlyTwo)...);

---Array
alias OnlyTwo = AliasSeq!(10, 20);
pragma(msg, Values + OnlyTwo);


The idea is simply if we have:

    t op c

where t is a tuple and c is not, the result is:

   tuple(t[0] op c, t[1] op c, ..., t[length - 1] op c)

For:

    t1 op t2

the result is:

   tuple(t1[0] op t2[0], t1[1] op t2[1], ..., t1[length - 1] op t2[length - 1])

The AST doesn't have to be walked to make this work, just do it as part of the usual bottom-up semantic processing.


The advantage is:

1. no new grammar
2. no new operator precedence rules
3. turn expressions that are currently errors into doing the obvious thing


Why does C++ use ... rather than array syntax? Because C++ doesn't have arrays!