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.