January 05, 2022
On 05.01.22 07:33, Walter Bright wrote:
> On 1/3/2022 7:09 PM, Timon Gehr wrote:
>> My DIP draft extends "alias this" to allow this (which should work anyway):
>>
>> alias Seq(T...)=T;
>> struct S{
>>      byte b;
>>      int c;
>>      alias this=Seq!(b,c);
>> }
>>
>> void func(byte,int);
>>
>> func(S());
> 
> I confess, this makes me uneasy. `alias this` produced a lot of unintended consequences. I'm gunshy of extending it. I'd prefer:
> 
>      func(S().tupleof);
> ...

Well, then let's not extend `alias this`.

> 
>> It does have some significant drawbacks, e.g. `foo(int,int)` and `foo((int,int))` would have a different ABI and the second overload can't be called as `foo(1,2);`...
> 
> I tried to actively avoid that :-)
> 
>> To be very clear, I want this:
>>
>> [(1,2),(3,4)].map!((a,b)=>a+b).each!writeln;
>>
>> An this is generally what people mean when they talk about "tuples".
> 
> I understand. It looks like a worthy goal.

How about something like opArgs, dealing specifically with this case? (i.e., a function call `foo(x)` with a single argument is immediately rewritten to `foo(x.opArgs)` if `x` has a member `opArgs`, and this rewrite is applied exactly once.)
January 05, 2022
On 05.01.22 08:28, Timon Gehr wrote:
>>
>>> To be very clear, I want this:
>>>
>>> [(1,2),(3,4)].map!((a,b)=>a+b).each!writeln;
>>>
>>> An this is generally what people mean when they talk about "tuples".
>>
>> I understand. It looks like a worthy goal.
> 
> How about something like opArgs, dealing specifically with this case? (i.e., a function call `foo(x)` with a single argument is immediately rewritten to `foo(x.opArgs)` if `x` has a member `opArgs`, and this rewrite is applied exactly once.)

(This would apply symmetrically at the call site, so that if we define a function `foo(T x)`, where T has an opArgs, it's `foo(typeof(T.opArgs) x)` instead.)
January 05, 2022
On Wednesday, 5 January 2022 at 06:33:39 UTC, Walter Bright wrote:
> On 1/3/2022 7:09 PM, Timon Gehr wrote:
>> To be very clear, I want this:
>> 
>> [(1,2),(3,4)].map!((a,b)=>a+b).each!writeln;
>> 
>> An this is generally what people mean when they talk about "tuples".
>
> I understand. It looks like a worthy goal.

Is this really necessary or even desirable? Tuples do not auto-expand like this in other languages. For example, in Python:

    def fun(*args):
        print(len(args))

    tup = (1, 2, 3)
    fun(1, 2, 3) # prints "3"
    fun(tup) # prints "1"

Even without automatic tuple expansion, Timon's example could be written as follows:

    alias apply(alias fun) = (args) => fun(args.tupleof);
    [(1,2), (3,4)].map!(apply!((a, b) => a+b)).each!writeln;
January 05, 2022
On Wednesday, 5 January 2022 at 07:28:41 UTC, Timon Gehr wrote:
>
> How about something like opArgs, dealing specifically with this case? (i.e., a function call `foo(x)` with a single argument is immediately rewritten to `foo(x.opArgs)` if `x` has a member `opArgs`, and this rewrite is applied exactly once.)

This mechanism seems too powerful to me; for example, one could write code like the following:

    struct S {
        string opArgs;
    }

    string fun(S s) { return "S overload"; }
    string fun(string s) { return "string overload"; }

    void main() {
        assert(fun(S()) == "S overload"); // fails
    }

If there is to be any mechanism for automatic expansion of tuples, it should probably be narrow enough to avoid enabling surprises like this one.
January 06, 2022
On 1/5/22 23:37, Paul Backus wrote:
> On Wednesday, 5 January 2022 at 06:33:39 UTC, Walter Bright wrote:
>> On 1/3/2022 7:09 PM, Timon Gehr wrote:
>>> To be very clear, I want this:
>>>
>>> [(1,2),(3,4)].map!((a,b)=>a+b).each!writeln;
>>>
>>> An this is generally what people mean when they talk about "tuples".
>>
>> I understand. It looks like a worthy goal.
> 
> Is this really necessary or even desirable? Tuples do not auto-expand 

This is not about auto-expanding. Conceptually, functions with multiple arguments are functions that take a tuple argument. I am sure you are aware of this.

> like this in other languages.

Well, in _some_ languages. Those languages get it wrong.

> For example, in Python:
> 
>      def fun(*args):
>          print(len(args))
> 
>      tup = (1, 2, 3)
>      fun(1, 2, 3) # prints "3"
>      fun(tup) # prints "1"
> ...

Yes, and I dislike this about Python. Making multiple arguments a distinct concept from tuples on the surface is just not particularly good design. Why is _that_ desirable? It's a good example of non-orthogonal language design.

```d
void foo(int a, string b){} // pattern (int a,string b)
foo(1,"2"); // match tuple (1,"2")

(int a, string b) = (1,"2"); // same thing

void foo((int a, string b), double c){}

foo((1,"2"), 3.0);

((int a,string b), double c) = ((1,"2"),3.0);
```

Why would pattern matching tuples work differently in a parameter list and elsewhere?

> Even without automatic tuple expansion, Timon's example could be written as follows:
> 
>      alias apply(alias fun) = (args) => fun(args.tupleof);
>      [(1,2), (3,4)].map!(apply!((a, b) => a+b)).each!writeln;

Clearly, but who wants to do that?
January 06, 2022
On 1/5/22 23:42, Paul Backus wrote:
> On Wednesday, 5 January 2022 at 07:28:41 UTC, Timon Gehr wrote:
>>
>> How about something like opArgs, dealing specifically with this case? (i.e., a function call `foo(x)` with a single argument is immediately rewritten to `foo(x.opArgs)` if `x` has a member `opArgs`, and this rewrite is applied exactly once.)
> 
> This mechanism seems too powerful to me; for example, one could write code like the following:
> 
>      struct S {
>          string opArgs;
>      }
> 
>      string fun(S s) { return "S overload"; }
>      string fun(string s) { return "string overload"; }
> 
>      void main() {
>          assert(fun(S()) == "S overload"); // fails
>      }
> 
> If there is to be any mechanism for automatic expansion of tuples, it should probably be narrow enough to avoid enabling surprises like this one.

Why is that a surprise? You could similarly do something like:

alias S=AliasSeq!(string);
January 06, 2022

On Wednesday, 5 January 2022 at 22:42:14 UTC, Paul Backus wrote:

>

On Wednesday, 5 January 2022 at 07:28:41 UTC, Timon Gehr wrote:

>

How about something like opArgs, dealing specifically with this case? (i.e., a function call foo(x) with a single argument is immediately rewritten to foo(x.opArgs) if x has a member opArgs, and this rewrite is applied exactly once.)

This mechanism seems too powerful to me; for example, one could write code like the following:

struct S {
    string opArgs;
}

string fun(S s) { return "S overload"; }
string fun(string s) { return "string overload"; }

void main() {
    assert(fun(S()) == "S overload"); // fails
}

If there is to be any mechanism for automatic expansion of tuples, it should probably be narrow enough to avoid enabling surprises like this one.

Yeah, and what does "exactly once" mean here?


struct S {
         string opArgs;
     }

     string fun(S s) { return "S overload"; }
     string fun(string s) { return "string overload"; }

     void main() {
         S s = S();
         assert(fun(s) == "string overload"); // succeeds
         assert(fun(s) == "S overload"); // this also succeeds??

     }
January 06, 2022
On Wednesday, 5 January 2022 at 23:56:03 UTC, Timon Gehr wrote:
> On 1/5/22 23:42, Paul Backus wrote:
>> On Wednesday, 5 January 2022 at 07:28:41 UTC, Timon Gehr wrote:
>>>
>>> How about something like opArgs, dealing specifically with this case? (i.e., a function call `foo(x)` with a single argument is immediately rewritten to `foo(x.opArgs)` if `x` has a member `opArgs`, and this rewrite is applied exactly once.)
>> 
>> This mechanism seems too powerful to me; for example, one could write code like the following:
>> 
>> [...]
>
> Why is that a surprise? You could similarly do something like:
>
> alias S=AliasSeq!(string);

Perhaps this is a better illustration:

    struct A {
        B opArgs() { return B(); }
    }

    struct B {}

    string fun(A) { return "A"; }
    string fun(B) { return "B"; }

    void main() {
        assert(fun(A()) == "A"); // fails
    }

It's perfectly logical if you know about opArgs and have the definition of A in front of you, but it's extremely surprising and unintuitive if you don't.
January 06, 2022

On Thursday, 6 January 2022 at 02:56:52 UTC, Tejas wrote:

>

On Wednesday, 5 January 2022 at 22:42:14 UTC, Paul Backus wrote:

>

On Wednesday, 5 January 2022 at 07:28:41 UTC, Timon Gehr wrote:

>

How about something like opArgs, dealing specifically with this case? (i.e., a function call foo(x) with a single argument is immediately rewritten to foo(x.opArgs) if x has a member opArgs, and this rewrite is applied exactly once.)

[...]

Yeah, and what does "exactly once" mean here?

I assume it means "once per argument"; i.e., the rewrite is not applied recursively to its own result. For example:

struct A { B opArgs; }
struct B { C opArgs; }
struct C {}

string fun(A) { return "A"; }
string fun(B) { return "B"; }
string fun(C) { return "C"; }

void main() {
    assert(fun(A()) == "B");
}

A() is rewritten to A().opArgs, but A().opArgs is not rewritten to A().opArgs.opArgs.

January 06, 2022
On Wednesday, 5 January 2022 at 22:42:14 UTC, Paul Backus wrote:
> On Wednesday, 5 January 2022 at 07:28:41 UTC, Timon Gehr wrote:
>>
>> How about something like opArgs, dealing specifically with this case? (i.e., a function call `foo(x)` with a single argument is immediately rewritten to `foo(x.opArgs)` if `x` has a member `opArgs`, and this rewrite is applied exactly once.)
>
> This mechanism seems too powerful to me; for example, one could write code like the following:
>
>     struct S {
>         string opArgs;
>     }
>
>     string fun(S s) { return "S overload"; }
>     string fun(string s) { return "string overload"; }
>
>     void main() {
>         assert(fun(S()) == "S overload"); // fails
>     }
>
> If there is to be any mechanism for automatic expansion of tuples, it should probably be narrow enough to avoid enabling surprises like this one.

You could just make sure that the root type is always used if an overload is available for it.

Just like if you did:

```d
struct S {
  string opArgs;
  alias opArgs this;
}
```