On 10.10.2017 17:05, Steven Schveighoffer wrote:
On 10/9/17 11:22 AM, Timon Gehr wrote:
On 09.10.2017 01:20, Steven Schveighoffer wrote:

My questioning comes with this:

void bar(int a);
void bar((int,) x);

To me, it is confusing or at least puzzling that these two aren't the same.
...

Well, to me it is a bit confusing that this is puzzling to you. Why should int be the same as (int,)? It does not make sense to index an integer, but (int,) can be indexed with 0 to get an integer.

I understand why (int,) is different from int. What I meant was, why can't I *call* a function that takes a single int tuple with a single int value?
...

Because this would require a special-case rule that I had not considered so far. This is up to discussion though.

I interpreted your question to be: "Why do your proposed rules not lead to my expected behaviour?", and not: "Why do your rules not allow this?", but it seems I have misinterpreted your question. Sorry for the confusion! :)

It shouldn't matter to the caller whether you plan to fiddle with your parameter via tuple syntax or directly with a value.
...

I see. I think what you propose does make sense, as it might smoothen out the interaction with other D language features such as variadics and overloading.

Again, I go back to the 2-parameter version. I can call it with 2 values, or a tuple of 2 values.

With the caveat that those two cases are actually identical, yes.

auto x = (1,"2"); // construct value
f(x); // now call f with value

and

f(1,"2"); // construct tuple and call f with the resulting value

It is like:

auto x = [1,2];
f(x);

and

f([1,2]);

Except that redundant parentheses are optional:
f(1,"2") is exactly equivalent to f((1,"2")) after parsing.

The second expression just adds an additional pair of parentheses around f. f(((1,"2"))) is also the same expression for the same reason.

Note that this does not compile:

void f(int a,int b,int c){}
f(1,(2,3));

The reason is that I tried to call f with an argument of type (int,(int,int)), while it expected an argument of type (int,int,int).

It makes no difference to the callee how I call it, as long as I put 2 values on the stack.
...

Well, I think it should maybe not be possible to conflate e.g. (int,int) and ((int,),(int,)).

I don't see why it should be different for a single parameter function.
...

I think you are making a strong point here.

To put it another way, in your scheme, what is the benefit to overloading a single value function call with a function call that takes a single element tuple? When would this be useful?
...

I agree that this is actually not useful.

Note that this means the following code will be accepted also:

void foo(int x,int y,int z){}

foo((1,2,3),);

Does this match your expectation?

Currently, I can call this:

foo(T...)(T t) if (T.length == 1) // function that takes a single element tuple

like this:

foo(1);

Why is this disallowed in your tuple scheme?
...

I take this to mean, why does the following code not compile:

void foo(T)(T t) if(T.length == 1) { ... }

foo(1);

Nope, I meant my original. A "tuple" as D currently uses it, can have exactly one element, and I can call that function with exactly one value. I don't have to call it as:

AliasSeq!(int) v;
v[0] = 1;
foo(v);

Which is analogous to your requirements (obviously, D is missing the syntax for tuple literals, which is why it's complicated).

Note that if foo is:

foo(int x);

I can still call it with v. I don't see why we can't keep these kinds of allowances.
...

I see. Well, we can't keep them to the extent AliasSeq has them. AliasSeq always auto-expands. Auto-expansion for tuples can become a problem, especially in generic code, because it forgets structure information. For example:

void printElements(T)(T[] arr){
    foreach(x;enumerate(a)){
        print("at index ",x[0]," we have ",x[1]);
    }
}

auto a = [(1,2),(3,4),(5,6)];
printElements(a);

With auto-expansion, this prints:
at index 0, we have 1
at index 1, we have 3
at index 2, we have 5

However, it is quite clear from the definition of printElements that the programmer wanted it to print:

at index 0, we have (1,2)
at index 1, we have (3,4)
at index 2, we have (5,6)

AliasSeq does not have this specific problem, because it cannot be put into an array without expanding.

I would think single value tuples and single values would be pretty much interchangeable.

Well, no. Otherwise 2[0] would be allowed and equal to 2. And then, what would [2][0] be? [2] or 2?

Not interchangeable in terms of usage, but interchangeable in terms of overloading.

What I would have expected is for foo(int) and foo((int,)) to be equivalent mangling (like the bar(int, int) and bar((int, int)) are equivalent), and for the caller to be able to call those functions with either a single value or a singleton tuple.

Inside the function, of course, they are treated differently as the callee decides whether to unpack the tuple or not via the parameters.


This makes sense, and I think your proposal improves the design.

Reply via email to