April 28, 2020
On Monday, 27 April 2020 at 22:15:53 UTC, Walter Bright wrote:
> On 4/27/2020 7:41 AM, Atila Neves wrote:
>> On Monday, 27 April 2020 at 13:02:03 UTC, jmh530 wrote:
>>> On Monday, 27 April 2020 at 12:08:11 UTC, Atila Neves wrote:
>>>> [snip]
>>>>
>> 
>> I know. Eigen is literally the reason why I can't convince any friends who work at CERN to try D. My dream would be to have dpp somehow enable calling it from D, but I'm not sure that'll ever be possible.
>
> Actually, you can write expression templates in D (I've done it as a demo). But it is a bit more limited in D because D doesn't allow separate overloads for < <= > >=, for example.

These limitations are one of the reasons why I'm not sure it's possible to call into Eigen. Another is trying to translate C++ features that don't exist in D such as reference types.

> A far as Boost Spirit goes, more than one person has made a D parser generator using mixins that is far better.

I don't think D is even in the same league here and is obviously better. My point was that ETs are alive and well in C++.
May 07, 2020
I think I just had a use for this.

I have a `const Rectangle` and wanted to shift the whole thing down and right one unit.

Wrote:

Rectangle(r.l + 1, r.t + 1, r.b + 1, r.r + 1);


Would have been kinda cool if I could have done

Rectangle((r.tupleof + 1)...)


I don't think that can be done today since functions cannot return tuples and templates cannot use runtime values.

Though well it perhaps could be done today with something like

auto tupleMap(alias what, T...)(T t) {
     struct Ret { T pieces; }
     foreach(ref arg; t)
        arg = what(arg);
     return Ret(t);
}

Rectangle(tupleMap!(a => a+1)(r.tupleof).tupleof);


I think that'd work and it isn't recursive template instantiation but it is a bit wordy.

I think this new static map thing could be useful for runtime values as well as template things.
May 08, 2020
Another potential use for this would be writing type translation functions.

I've written something along these lines many times:

     ParameterTypeTuple!T fargs;
     foreach(idx, a; fargs) {
           if(idx == args.length)
                   break;
     cast(Unqual!(typeof(a))) fargs[idx] = args[idx].get!(typeof(a));


This converts arguments of a dynamic type to the static parameter types of a given function in preparation to call it.

That ugly cast on the lhs there is to deal with const.

     void foo(const int a) {}

That function there needs the cast to assign to the const param.

Well, with the static map, we MIGHT be able to just do

foo(fromDynamic(fargs, args.pop)...)

or something like that which expands in place so the mutable thing is implicitly cast to const without the explicit cast intermediate.


I haven't tried that btw i just think it might work given the concept. That would be impossible with staticMap as-is because of the runtime variable in there.
May 08, 2020
On Wednesday, 22 April 2020 at 13:37:26 UTC, Manu wrote:
> On Wed, Apr 22, 2020 at 11:35 PM rikki cattermole via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
>
>> On 23/04/2020 1:22 AM, Steven Schveighoffer wrote:
>> > On 4/22/20 8:19 AM, rikki cattermole wrote:
>> >> Change it to something like .unpackSequence instead of ... and I would be happy.
>> >
>> > unpackSequence is a valid identifier. That would be a breaking change. Plus it would be less obvious. If anything it would have to be unpackSequence!(expr).
>> >
>> > Plus there is precedence with C++ for using ...
>> >
>> > And it makes intuitive sense -- you define tuples with T...
>> >
>> > -Steve
>>
>> Yeah.
>>
>> But it also has precedence with type properties such as mangleof instead of the approach C took with sizeof and friends.
>>
>> Right now we use it in parameters, adding it to the argument side with a completely different meaning ugh.

You can distinguish "Types... params" from "Type[] params..." by where the dots are.
In contrast to C and C++, D has a very strong principle that everything after the parameter (or declared variable) is not part of the type. So in the latter case, the dots clearly don't belong to the type.

> I feel it's a perfectly natural expansion of the existing meaning. I'm 300% happy with the syntax personally, I will have a tantrum if you take it away from me.

The syntax is perfectly fine for me. Don't be distracted by a single opinion. The ... token currently can only be used in (both template and function) parameter declarations (and `is` expressions where they are kind of the same as template parameters). You're introducing it in an expression and/or type-expression context which seems to be fine, but very technically, it's not. There's one example where it clashes: Type-safe variable argument functions that omit their last parameter's name.

One thing I want to mention: Let Ts be the type sequence int, long, double[]; then

    void f(Ts[]...);

becomes ambiguous. It could mean (by the new proposal)

    void f(int[] __param_0, long[] __param_1, double[][] __param_2);

or (by the current language rules)

    void f(int __param_0, long __param_1, double[] __param_2...);

I'm pointing it out, because it is theoretically a breaking change with silent change of behavior. I doubt that any code base uses that pattern purposefully since it is extremely awkward and shouldn't make it through any code review.
May 08, 2020
On 5/8/20 1:13 PM, Q. Schroll wrote:

> or (by the current language rules)
> 
>      void f(int __param_0, long __param_1, double[] __param_2...);
> 
> I'm pointing it out, because it is theoretically a breaking change with silent change of behavior. I doubt that any code base uses that pattern purposefully since it is extremely awkward and shouldn't make it through any code review.

Ugh. I doubt it would be a breaking change, as Ts... would suffice (if you really wanted to "abuse" that feature). As in, it won't break any existing code, because nobody would do that.

I'd say it's fine for the DIP to take over that syntax.

-Steve
May 08, 2020
On Wednesday, 22 April 2020 at 16:49:58 UTC, Steven Schveighoffer wrote:
> On 4/22/20 10:37 AM, Manu wrote:
>> I think efficient implementation here would depend on a static fold, which I plan for a follow-up, and it's a very trivial expansion from this DIP.
>> Static reduce would allow `...` as an argument to a BinOp
>> Ie; `Tup + ...` would expand `Tup[0] + Tup[1] + Tup[2] + ...`
>> You could do `is(FindType == Tup) || ...`, and it would evaluate true if FindType exists in Tup, with no junk template instantiations!
>
> This is awesome, and I'm not seeing why you would save it for later.
>
> In general, couldn't this DIP be done just strictly on binary operators and do what this DIP does with commas?
>
> i.e.
>
> foo(T) , ... expands to foo(T[0]), foo(T[1]), ..., foo(T[n])
>
> -Steve

That's a good addition. Note that C++ requires parentheses around the fold expressions. I think that's a good thing; makes stuff more readable. But I'd think when the fold expression is completely inside a construct that has parentheses, the fold expression parentheses should be optional. I.e. you'd need

    bool condition = (f(tuple) && ...); // unfolds to f(tuple[0]) && _ _ _ && f(tuple[$-1])

but you can do

    if (f(tuple) && ...) { /*whatever*/ }

and don't need

   if ((f(tuple) && ...)) { /*whatever*/ }

which is just unnecessary and confusing.

Also note that C++ allows initial and terminal values: (1 + ... + f(tuple)) unfolds to (1 + f(tuple[0]) + _ _ _ + f(tuple[$-1])). Some operators don't have default values and need something to handle empty tuples. (To be clear: Exactly one expression must contain tuples.)
May 08, 2020
On Friday, 8 May 2020 at 17:19:18 UTC, Steven Schveighoffer wrote:
> I doubt it would be a breaking change, as Ts... would suffice (if you really wanted to "abuse" that feature). As in, it won't break any existing code, because nobody would do that.

That's what I said. As I understand the document explaining what a DIP needs to address, silent change of behavior *must* be addressed in a DIP.

> I'd say it's fine for the DIP to take over that syntax.

Sure.

May 08, 2020
There are some corner cases I'd like to have an answer to:

    void f(int i, long l) { }

    import std.typecons : Tuple;

    alias Tup = Tuple!(int, long);
    Tup t = Tup.init;

    f(t...); //?

Would that //? line work, i.e. use `alias expand this` of std.typecons.Tuple? (Currently, without the dots, it doesn't work!) If so, say I have nested tuple types like these:

    alias AB = Tuple!(A, B);
    alias CD = Tuple!(C, D);
    alias T = Tuple!(AB, CD);

    void g(AB ab, CD cd) { }
    void h(A a, B b, C c, D d) { }

    T t = T.init;
    g(t...); //1  t... should be (t.expand[0], t.expand[1])
    h(t...); //2
    h((t...)...); //3  (t...)... should be (t.expand[0].expand[0], t.expand[0].expand[1], t.expand[1].expand[0], t.expand[1].expand[1])

I'd expect //1 to work. Does `...` expand multiple times if it needs to like in //2 or would I need to do the thing in //3? Would //3 even work?

I'm asking about stuff that C++ wouldn't need to care about.

What does t.expand... mean? Without much thinking, I'd expect (t.expand[0], t.expand[1]), but if `...` really walks into the expression, seeing `.` as a binary operator, it should be expanding `t` and `expand` in parallel, for `t` using its alias this. That way, it boils down to (t.expand[0] . expand[0], t.expand[1] . expand[1]).

Should we be allowed to write (t.expand)... and t.(expand...) to clarify what we mean?

(I want this DIP to succeed, so I'll walk into every corner and expose the cases for them to be addressed.)
May 08, 2020
On Friday, 8 May 2020 at 20:53:39 UTC, Q. Schroll wrote:
>     alias Tup = Tuple!(int, long);

This is just a struct, the ... shouldn't do anything to it (probably should be a syntax error).

The ... thing is all about compiler tuples which are unfortunately named the same but totally separate to library tuples.
May 09, 2020
On Sat, May 9, 2020 at 1:50 AM Adam D. Ruppe via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> Another potential use for this would be writing type translation functions.
>
> I've written something along these lines many times:
>
>       ParameterTypeTuple!T fargs;
>       foreach(idx, a; fargs) {
>             if(idx == args.length)
>                     break;
>       cast(Unqual!(typeof(a))) fargs[idx] =
> args[idx].get!(typeof(a));
>
>
> This converts arguments of a dynamic type to the static parameter types of a given function in preparation to call it.
>
> That ugly cast on the lhs there is to deal with const.
>
>       void foo(const int a) {}
>
> That function there needs the cast to assign to the const param.
>
> Well, with the static map, we MIGHT be able to just do
>
> foo(fromDynamic(fargs, args.pop)...)
>
> or something like that which expands in place so the mutable thing is implicitly cast to const without the explicit cast intermediate.
>
>
> I haven't tried that btw i just think it might work given the concept. That would be impossible with staticMap as-is because of the runtime variable in there.
>

Yes! I use this pattern _all the time_ in C++, and it's definitely a
motivating use case for `...` as I see it.
This is an intended and important use case.