Sorry for waiting so long to respond, I had to think about this a lot...

On 10/12/17 3:05 PM, Timon Gehr wrote:
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! :)

Not a problem!

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));

Right, there is a structural difference here. The 2 and 3 being tied together is a piece of information that is lost or different than is expected. I get that, and agree with it.


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,)).

This, I'm not 100% sure on. In my mind, the difference between a value and a tuple of one element is trivial and negligible. I think they should be implicitly convertible between each other. Perhaps they would still be mangled differently, but you could still call both with the different forms. Sort of like const(int) and int have different semantics, but I can call a function with one or the other.

In that sense, perhaps foo(int) and foo((int,)) are different manglings, but you can still call either form with either form.

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?

Yes, that seems reasonable to me.


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.

Right, I was specifically focusing on what I understood -- the existing mechanism of "singleton tuple" in D.

But really, I think implicit expanding or tupling of values when needed would fix all the problems I had, fits into the current expectations of D users, and allows your scheme to work.

So to recap my thoughts:

1. I think T and (T,) can be different types, probably should be mangled differently, but implicitly cast to one another, similar to T and const(T) when T is a value type.
2. I'm OK with fun(T,T) and fun((T,T)) being equivalent.
3. I'm still not in love with (T,) as a singleton tuple type, it looks like you accidentally put in a trailing comma. But I don't have a better syntax to suggest. 4. I'm still convinced that having a template type match a tuple when multiple parameters are passed as in:

foo(T)(T t) {...}

foo(1, 2); // OK, T = (int, int)

is not going to go well with existing code.

On point 4, as a compromise, would it be possible to opt-in to such things via a new syntax? For example something like:

foo((T,...))(T t) // T is a tuple of 0 or more items.

One other thing, what does an empty tuple look like? Is it (,)?

-Steve

Reply via email to