This is a complex topic, and in this post I am not able to
discuss everything that needs to be discussed. So I will discuss
only part of the story.
First: tuples are important enough. I think they should be
built-in in a modern language, but maybe having them as
half-built-in will be enough in D. Currently in D we have
(deprecated) built-in complex numbers that I use only once in a
while, and half-usable library defined tuples that I use all the
time.
Second: removing comma operator from D has some advantages
unrelated to tuple syntax. Even disallowing bad looking C-like
code that uses commas is an improvement by itself (but maybe it's
not a big enough improvement...).
Third: replacing the packing syntax tuple(x,y) with (x,y) is nice
and maybe even expected in a partially functional language as D,
but that's _not_ going to improve D usability a lot. What I am
asking for is different: I'd like D tuples to support handy
unpacking syntax:
1) In function signatures;
2) In foreach;
3) At assignment points;
4) In switch cases.
Note: in the examples below I have used the tuple(x,y) syntax for
simplicity, feel free to replace it with a shorter (x,y) syntax
if you want.
---------------------------
1) This tuple unpacking use case is important, and it's not what
you argue against in the DIP:
int f(tuple(int x, int y)) { return x + y; }
auto pairs = zip([10, 20], [2, 3]);
map!f(pairs)
This is different from what you are arguing against because this
"f" is not accepting two normal ints, it accepts a tuple made of
two ints, and names them "x" and "y" on the fly.
---------------------------
2)
auto pairs = zip([10, 20], [2, 3]).array();
foreach (i, tuple(x, y); pairs) {}
---------------------------
3) This is probably the most common use case:
auto foo() { return tuple(10, 20); }
auto tuple(x, y) = foo(); // bad syntax
auto (x, y) = foo(); // better syntax
void main() {
auto a = tuple(1, 2);
auto tuple(x1, x2) = a; // bad syntax
auto (y1, y2) = a; // better syntax
}
---------------------------
4) This looks simple, but allows things like using BigInt in
switch cases, implementing a very simple but quite handy
pattern-matching, etc:
auto v = tuple(10, 20);
final switch (v) {
case tuple(5, y): { x in scope... } break; // y is not a
global
case tuple(x, y): { ... } break; // this covers all cases
}
---------------------------
There are other usage patterns that are handy, but in my opinion
the four I have shown here are the most commonly useful ones. See
also Bugzilla issues 6365 and 6367, they shows some other cases
and ideas and problems.
---------------------------
Tuple singletons: using (1,) as in Python is acceptable. using
(1) is not acceptable in my opinion, too much dangerous. tuple(1)
is also acceptable, it's longer, but it's not commonly used, so
it's OK.
Empty tuples: the (,) syntax proposed in the DIP is not nice, it
seems to have one invisible item, but maybe it's acceptable.
tuple() is also acceptable, it's longer, but it's not commonly
used, so it's OK.
In the end tuples with 0 and 1 items are sometimes useful, but
they are not nearly as useful as supporting well tuples with 2 or
more items. Scala language agrees with this.
---------------------------
Tuple slicing: it's not a fundamental operation, but it's nice to
try to make it work correctly :-) I think not even Haskell does
this well.
---------------------------
Summary:
- I think (1,2) is nicer and better than tuple(1,2), and I'd like
to have such syntax, but it's not a large improvement and it's
not what I am asking for now (less priority).
- Supporting tuples with 0 and 1 items is sometimes handy but I
think it's not essential.
- On the other hand syntax to unpack/destructure tuples in many
situations is important for allowing a proper and handy use of
tuples in D.
Bye,
bearophile

On Sunday, 23 September 2012 at 21:37:06 UTC, Adam D. Ruppe wrote:
> I'm not for removing the comma operator, but it occurs to me we
> could do it in the library:
>
> auto commaOperatorReplacement(T...)(T t) {
> return t[$-1];
> }
>
> There might be some edge case where that wouldn't work, but I
> think it works in most cases.
If D is like C in this regard, then the function above cannot
replace comma operator, because the order of evaluation is
defined for comma operator, but not for function parameters. You
could use something like that, though:

On Sunday, 23 September 2012 at 22:29:31 UTC, jerro wrote:
> On Sunday, 23 September 2012 at 21:37:06 UTC, Adam D. Ruppe
> wrote:
>> I'm not for removing the comma operator, but it occurs to me
>> we could do it in the library:
>>
>> auto commaOperatorReplacement(T...)(T t) {
>> return t[$-1];
>> }
>>
>> There might be some edge case where that wouldn't work, but I
>> think it works in most cases.
>
> If D is like C in this regard, then the function above cannot
> replace comma operator, because the order of evaluation is
> defined for comma operator, but not for function parameters.
> You could use something like that, though:
Sorry about the "You could use something like that, though" part.
I realized "something like that" wouldn't work either, but forgot
to delete that text.

On Monday, September 24, 2012 00:30:27 jerro wrote:
> If D is like C in this regard, then the function above cannot
> replace comma operator, because the order of evaluation is
> defined for comma operator, but not for function parameters.
I believe that it's currently undefined for D, but Walter wants to define it so
that it's left-to-right in an effort to eliminate bugs resulting from the
varying order of function argument evaluation. He just hasn't gotten around to
doing it yet.
- Jonathan M Davis

On 09/23/2012 10:40 PM, Andrei Alexandrescu wrote:
> I discussed this with Walter, and we concluded that we could deprecate
> the comma operator if it helps tuples. So I started with this:
>
> http://www.prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP19
>
> Unfortunately, I started much cockier than I ended. The analysis in
> there fails to construct a case even half strong that deprecating the
> comma operator could significantly help tuples.
That is because it does not base the discussion on the right
limitations of built-in tuples:
auto (a,b) = (1,"3");
(auto a, string b) = (1, "3");
BTW: the following works
Tuple!(int, string) t2 = t1[0 .. 2];
because of this:
=> (alias this)
Tuple!(int, string) t2; t2._fields = t1[0 .. 2];
=> (tuple assignment)
Tuple!(int, string) t2; t2._fields[0]=t1[0]; t2._fields[1]=t1[1];
> Well it essentially
> concludes that tuples are mostly fine as they are, and attempts to
> embellish them syntactically are marred with unexpected problems.
> Nevertheless, I sure have missed aspects all over, so contributions are
> appreciated.
>
- We already use the name 'tuple'. I'd suggest renaming that to
'sequence' or similar. template Seq(T...){ alias T Seq; }
- The empty tuple can well be (), just like 'Seq!()' works without
issues (it is an expression that is also a type). What is wrong with
it?
- How do we expand a sequence into a tuple?
=> (Seq!(1,2,3),)
- What is the calling convention used for passing built-in tuples to
and from functions?
- As tuples are built-in, expansion can be shorter than '.expand'.
foo(1, tup..., 3); ?
- Template tuple parameters? This would work, but...
template Tuple((T...,)){ alias (T,) Tuple; }
void bar(T,(U...,),V...)(T delegate(U) dg, V args){ ... }
void foo(T,(U...,),(V...,))(T delegate(U) dg, V args){
bar!(T,Tuple!U,V)(dg, args);
} // U and V can be passed separately
- Named tuple fields?
(int x, int y) tuple = (1,2);
swap(tuple.x, tuple.y);

On 9/23/12 5:57 PM, deadalnix wrote:
> Le 23/09/2012 23:52, Nick Sabalausky a écrit :
>> Ok, here's a crazy idea:
>>
>> Do the reasons for explicit tuple-expansion necessarily apply to
>> zero- and one-element tuples? I'm not so sure. Suppose we allowed
>> implicit expansion on those...
>>
>> Now I know what you're thinking: That would be an ugly inconsistency
>> between tuples of sizes>1 vs<=1. Well, *mechanically* yes, but
>> consider this:
>>
>> *Logically* speaking, is there really any difference between a
>> one-element tuple and an ordinary single value? I don't think so, and
>> here's why: What is a tuple, logically speaking? Multiple values being
>> handled as if they were a single value. So what's a one-element tuple?
>> *One* value being handled as if it were one value - which is *is*.
>>
>> Similarly, a zero-element tuple is logically equivalent to void (or the
>> one value a void can have: the value void, a concept which has been
>> argued in the past that might be useful for D, particularly in
>> metaprogramming). (I admit this is a little weaker than my argument
>> for one-element tuples.)
>>
>> So perhaps zero- and one-element tuples should be implicitly
>> convertible back and forth with void and ordinary non-tuple values,
>> respectively (polysemous values?), because that's what they essentially
>> are.
>>
>
> My reflection on the subject for the past month lead me to the same
> conclusion.
This notion a lot of trouble with it; I think it's safe to abandon it
entirely.
Once a one-element tuple becomes equivalent to the actual item, there's
an explosion of trouble and special cases in the language and in code
that uses it. For example, divide and conquer code that manipulates
tuples and takes t[0 .. $/2] and t[$/2+1 .. $] would suddenly get to
cases in which the slices are no longer tuples, and so on. And that's
only the beginning.
Also, having no integrated notion of a zero-element tuple would again
mess with the algebra as much as the absence of 0 would hurt numbers.
It's just troublesome.
I appreciate the attraction of this idea, but again I think it's safe to
just not even discuss it.
Andrei

On 09/24/2012 12:40 AM, Jonathan M Davis wrote:
> On Monday, September 24, 2012 00:30:27 jerro wrote:
>> If D is like C in this regard, then the function above cannot
>> replace comma operator, because the order of evaluation is
>> defined for comma operator, but not for function parameters.
>
> I believe that it's currently undefined for D, but Walter wants to define it so
> that it's left-to-right in an effort to eliminate bugs resulting from the
> varying order of function argument evaluation. He just hasn't gotten around to
> doing it yet.
>
> - Jonathan M Davis
>
I believe it is currently left-to-right for D, in all kinds of
expressions, but DMD does not implement it yet.

On 9/23/12 6:42 PM, Timon Gehr wrote:
> That is because it does not base the discussion on the right
> limitations of built-in tuples:
>
> auto (a,b) = (1,"3");
> (auto a, string b) = (1, "3");
I meant to mention that but forgot. The interesting thing about this is
that, if we decide it's the main issue with today's tuples, we pull
Kenji's patch and close the case.
> BTW: the following works
>
> Tuple!(int, string) t2 = t1[0 .. 2];
>
> because of this:
>
> => (alias this)
>
> Tuple!(int, string) t2; t2._fields = t1[0 .. 2];
>
> => (tuple assignment)
>
> Tuple!(int, string) t2; t2._fields[0]=t1[0]; t2._fields[1]=t1[1];
Yah, I thought the writeup clarified that.
> - We already use the name 'tuple'. I'd suggest renaming that to
> 'sequence' or similar. template Seq(T...){ alias T Seq; }
Then what are the "old" tuples?
> - The empty tuple can well be (), just like 'Seq!()' works without
> issues (it is an expression that is also a type). What is wrong with
> it?
There's already intensive use of parens in D. I predict there's going to
be big trouble with "()" even assuming it's not technical ambiguous, for
example a lambda that returns an empty tuple would be "()() {...}" and
all that jazz.
> - How do we expand a sequence into a tuple?
> => (Seq!(1,2,3),)
I think we're discussing different things - the above seems to deal with
expression/alias tuples. DIP19 discusses strictly runtime value tuples.
> - What is the calling convention used for passing built-in tuples to
> and from functions?
I don't know. The current approach with .expand is nothing special - as
if the programmer wrote the expansion by hand.
> - As tuples are built-in, expansion can be shorter than '.expand'.
> foo(1, tup..., 3); ?
I find that sugar gratuitous.
> - Template tuple parameters? This would work, but...
> template Tuple((T...,)){ alias (T,) Tuple; }
>
> void bar(T,(U...,),V...)(T delegate(U) dg, V args){ ... }
> void foo(T,(U...,),(V...,))(T delegate(U) dg, V args){
> bar!(T,Tuple!U,V)(dg, args);
> } // U and V can be passed separately
>
> - Named tuple fields?
>
> (int x, int y) tuple = (1,2);
>
> swap(tuple.x, tuple.y);
I kinda got lost around all that.
Andrei

Le 24/09/2012 00:48, Andrei Alexandrescu a écrit :
> This notion a lot of trouble with it; I think it's safe to abandon it
> entirely.
>
> Once a one-element tuple becomes equivalent to the actual item, there's
> an explosion of trouble and special cases in the language and in code
> that uses it. For example, divide and conquer code that manipulates
> tuples and takes t[0 .. $/2] and t[$/2+1 .. $] would suddenly get to
> cases in which the slices are no longer tuples, and so on. And that's
> only the beginning.
>
This is a very weak point. In most cases, divide an conquer with tuple
don't even make sense.
> Also, having no integrated notion of a zero-element tuple would again
> mess with the algebra as much as the absence of 0 would hurt numbers.
> It's just troublesome.
>
> I appreciate the attraction of this idea, but again I think it's safe to
> just not even discuss it.
>
I'm not sure I want to answer that, so I wont.