www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP19: Remove comma operator from D and provision better syntactic

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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. 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.


Thanks,

Andrei
Sep 23 2012
next sibling parent reply "foobar" <foo bar.com> writes:
On Sunday, 23 September 2012 at 20:39:38 UTC, 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. 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.


 Thanks,

 Andrei

Yay! vote += infinity Regarding specifics, I'd argue that (int) should be a perfectly valid construct. I don't see what's confusing about that. I also am opposed to the idea to limit tuples to 2 or more elements. On the contrary, I root for the clean semantics of having a proper unit type represented as () instead of the horrible C semantics of the 'void' type. Just make void an alias to () for backwards compatibility. Also, why the need to special case the for loop? the increment part can be a regular tuple since the for loop doesn't for (int i, j; cond; i++, j++) {}
Sep 23 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/23/2012 10:58 PM, foobar wrote:
 On Sunday, 23 September 2012 at 20:39:38 UTC, 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. 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.


 Thanks,

 Andrei

Yay! vote += infinity Regarding specifics, I'd argue that (int) should be a perfectly valid construct.

If so, it should be the same as int.
 I don't see what's confusing about that.

Those are currently both valid and give the same result: int.max (int).max Anyway, the tuple type should resemble the tuple expression syntactically.
 I also am opposed to the idea to limit
 tuples to 2 or more elements. On the contrary, I root for the clean
 semantics of having a proper unit type represented as () instead of the
 horrible C semantics of the 'void' type. Just make void an alias to ()
 for backwards compatibility.

You mean, alias () to 'void' for backwards compatibility. How would that give 'clean semantics'? It's just syntax. Or is it about void supporting 'expand'? I don't think that makes sense. void foo(){} void main(){ void[] x = new int[42]; foo(x[0].expand); // whut? }
 Also, why the need to special case the for loop? the increment part can
 be a regular tuple since the for loop doesn't assign the value of the
 expression. for (int i, j; cond; i++, j++) {}

I assume Andrei intends the enclosing parens to be mandatory.
 I'd suggest looking at functional implementations such as ML or Haskel[l] for
more inspiration.

They just do the two most obvious things. (well, Haskell has to answer some questions related to the evaluation model that are irrelevant for D.) Neither one has a syntactic construct for building a single element tuple.
 Also, I want to add that type declarations should be changed from statements
to expressions so that we could do:
 auto tup = (3, "hello");
 (int num, string s) = tup; // num == 3, s == "hello"

I'd support the addition of declaration expressions (mainly for using them in conjuncts of if conditions), but this is not necessary to enable what you show above.
Sep 23 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/23/12, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> 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

About the (,) problem, what about using (void)? It's longer to type but it might be easier to humanly decode than trying to count lone commas. ((1, ), (,)) ((1, void), void, (void))
Sep 23 2012
prev sibling next sibling parent "foobar" <foo bar.com> writes:
On Sunday, 23 September 2012 at 20:57:48 UTC, foobar wrote:
 On Sunday, 23 September 2012 at 20:39:38 UTC, 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. 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.


 Thanks,

 Andrei


Yay! vote += infinity Regarding specifics, I'd argue that (int) should be a perfectly valid construct. I don't see what's confusing about that. I also am opposed to the idea to limit tuples to 2 or more elements. On the contrary, I root for the clean semantics of having a proper unit type represented as () instead of the horrible C semantics of the 'void' type. Just make void an alias to () for backwards compatibility. Also, why the need to special case the for loop? the increment part can be a regular tuple since the for loop doesn't assign the value of the expression. for (int i, j; cond; i++, j++) {} I'd suggest looking at functional implementations such as ML or Haskel for more inspiration.
Sep 23 2012
prev sibling next sibling parent "foobar" <foo bar.com> writes:
On Sunday, 23 September 2012 at 21:00:39 UTC, foobar wrote:
 On Sunday, 23 September 2012 at 20:57:48 UTC, foobar wrote:
 On Sunday, 23 September 2012 at 20:39:38 UTC, 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. 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.


 Thanks,

 Andrei


Yay! vote += infinity Regarding specifics, I'd argue that (int) should be a perfectly valid construct. I don't see what's confusing about that. I also am opposed to the idea to limit tuples to 2 or more elements. On the contrary, I root for the clean semantics of having a proper unit type represented as () instead of the horrible C semantics of the 'void' type. Just make void an alias to () for backwards compatibility. Also, why the need to special case the for loop? the increment part can be a regular tuple since the for loop doesn't assign the value of the expression. for (int i, j; cond; i++, j++) {} I'd suggest looking at functional implementations such as ML or Haskel for more inspiration.

urg.. .Sorry for the double posting.. Also, I want to add that type declarations should be changed from statements to expressions so that we could do: auto tup = (3, "hello"); (int num, string s) = tup; // num == 3, s == "hello"
Sep 23 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 23/09/2012 22:40, Andrei Alexandrescu a écrit :
 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. 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.


 Thanks,

 Andrei

This is a problem I think about for a while, and coding for SDC + studying how it is done in other languages has enlightened me on some points. I all for deprecating the comma operator. You have my full support on that point. As well as you have it for the « for » construct update. However, I'm not sure about everything you propose to implement tuples. I could discuss idea about 1 element tuples, or other tricky cases that deserve discussion (and I'm sure they will). But first, let me digress. D is already a really good language. One of its main flaw is the lack of 3rd party tools. The way new feature are implemented is a real problem for 3rd party tool developers. In other terms, the arm done by adding new feature is greater than any benefit new feature can bring us. Additionally, we have some open mess that need to be closed. Think about the -property madness, the holes in const/immutable transitivity, or other half buggy features. I consider that , operator should be deprecated then removed ASAP. It will limit the amount of code possibly broken in the future. But let's not introduce any new feature before at least introducing a versioning scheme that allow user to keep the version without the feature.
Sep 23 2012
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Sep 23 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Sep 23 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei
Sep 23 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 03:14, Andrei Alexandrescu a écrit :
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei

Is it by implementation or by design ?
Sep 23 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 03:41 AM, Jonathan M Davis wrote:
 On Monday, September 24, 2012 03:31:08 deadalnix wrote:
 Le 24/09/2012 03:14, Andrei Alexandrescu a écrit :
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei

Is it by implementation or by design ?

Design. It makes no sense for the left-hand side of an assignment to be evaluated before the right-hand side.

Of course it does. Some values are returned by dereferencing a hidden argument.
 It's function arguments that should be evaluated left-to-right.

 - Jonathan M Davis

Sep 23 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 04:27 AM, Timon Gehr wrote:
 On 09/24/2012 03:41 AM, Jonathan M Davis wrote:
 On Monday, September 24, 2012 03:31:08 deadalnix wrote:
 Le 24/09/2012 03:14, Andrei Alexandrescu a écrit :
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei

Is it by implementation or by design ?

Design. It makes no sense for the left-hand side of an assignment to be evaluated before the right-hand side.

Of course it does. Some values are returned by dereferencing a hidden argument.

Nevermind. In the general case, a temporary still has to be generated because of aliasing issues.
 It's function arguments that should be evaluated left-to-right.

 - Jonathan M Davis


Sep 23 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/23/12 9:31 PM, deadalnix wrote:
 Le 24/09/2012 03:14, Andrei Alexandrescu a écrit :
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei

Is it by implementation or by design ?

Currently probably neither :o). I used to oppose it, then I figured it's actually nice because of things like: int[int] stuff; stuff[42] = stuff.length; Andrei
Sep 23 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 05:25 AM, Andrei Alexandrescu wrote:
 On 9/23/12 9:31 PM, deadalnix wrote:
 Le 24/09/2012 03:14, Andrei Alexandrescu a écrit :
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei

Is it by implementation or by design ?

Currently probably neither :o). I used to oppose it, then I figured it's actually nice because of things like: int[int] stuff; stuff[42] = stuff.length; Andrei

So you are arguing for computing the arguments for a method call before the receiver is computed? (What about UFCS?) Or should opAssign behave specially?
Sep 24 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/24/12 8:18 AM, Timon Gehr wrote:
 On 09/24/2012 05:25 AM, Andrei Alexandrescu wrote:
 On 9/23/12 9:31 PM, deadalnix wrote:
 Le 24/09/2012 03:14, Andrei Alexandrescu a écrit :
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei

Is it by implementation or by design ?

Currently probably neither :o). I used to oppose it, then I figured it's actually nice because of things like: int[int] stuff; stuff[42] = stuff.length; Andrei

So you are arguing for computing the arguments for a method call before the receiver is computed? (What about UFCS?) Or should opAssign behave specially?

Ha, good point about opAssign! Probably we should go with left-to-right everywhere, including in assignment. Andrei
Sep 24 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 24, 2012 03:31:08 deadalnix wrote:
 Le 24/09/2012 03:14, Andrei Alexandrescu a =C3=A9crit :
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 =3D expr2, ex=


 gets evaluated first.
=20
 Andrei

Is it by implementation or by design ?

Design. It makes no sense for the left-hand side of an assignment to be= =20 evaluated before the right-hand side. It's function arguments that should be evaluated left-to-right. - Jonathan M Davis
Sep 23 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 03:14 AM, Andrei Alexandrescu wrote:
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei

Is this documented anywhere?
Sep 23 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/23/12 10:15 PM, Timon Gehr wrote:
 On 09/24/2012 03:14 AM, Andrei Alexandrescu wrote:
 On 9/23/12 7:20 PM, Adam D. Ruppe wrote:
 On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.

Actually it's right to left for assignments. In expr1 = expr2, expr2 gets evaluated first. Andrei

Is this documented anywhere?

Not to the best of my knowledge. I'm not even sure it's always the case. Andrei
Sep 23 2012
prev sibling next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
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.

That means (at least I think it means) that things like () or (1) or
((1)) require no way to disambiguate between tuple and expression,
because either way they're the same thing (or at least freely
convertible).
Sep 23 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
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.
 That means (at least I think it means) that things like () or (1) or
 ((1)) require no way to disambiguate between tuple and expression,
 because either way they're the same thing (or at least freely
 convertible).

Exactly.
Sep 23 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Sep 23 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
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.
Sep 23 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 01:08 AM, deadalnix wrote:
 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.

I agree with Andrei. Single element tuples need to support the same operations as tuples of other arities do.
Sep 23 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 01:28, Timon Gehr a écrit :
 I agree with Andrei. Single element tuples need to support the same
 operations as tuples of other arities do.

Obviously it should. The whole point is that you can implicitly cast a 1 element tuple into the element and vice versa. like : (int) a = (3); // a is a tuple of 1 element. int b = a; // Implicit tuple unpack. a = b; // Implicit tuple packing.
Sep 23 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 02:00 AM, deadalnix wrote:
 Le 24/09/2012 01:28, Timon Gehr a écrit :
 I agree with Andrei. Single element tuples need to support the same
 operations as tuples of other arities do.

Obviously it should. The whole point is that you can implicitly cast a 1 element tuple into the element and vice versa. like : (int) a = (3); // a is a tuple of 1 element. int b = a; // Implicit tuple unpack. a = b; // Implicit tuple packing.

Well, I do not want parentheses to randomly hide members. (a ~ b).length
Sep 23 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 02:21, Timon Gehr a écrit :
 On 09/24/2012 02:00 AM, deadalnix wrote:
 Le 24/09/2012 01:28, Timon Gehr a écrit :
 I agree with Andrei. Single element tuples need to support the same
 operations as tuples of other arities do.

Obviously it should. The whole point is that you can implicitly cast a 1 element tuple into the element and vice versa. like : (int) a = (3); // a is a tuple of 1 element. int b = a; // Implicit tuple unpack. a = b; // Implicit tuple packing.

Well, I do not want parentheses to randomly hide members. (a ~ b).length

Then it easy to add a comma to create a one element tuple. IE: (a ~ b).length == a.length + b.length (a ~ b,).length == 1
Sep 23 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 02:28 AM, deadalnix wrote:
 Le 24/09/2012 02:21, Timon Gehr a écrit :
 On 09/24/2012 02:00 AM, deadalnix wrote:
 Le 24/09/2012 01:28, Timon Gehr a écrit :
 I agree with Andrei. Single element tuples need to support the same
 operations as tuples of other arities do.

Obviously it should. The whole point is that you can implicitly cast a 1 element tuple into the element and vice versa. like : (int) a = (3); // a is a tuple of 1 element. int b = a; // Implicit tuple unpack. a = b; // Implicit tuple packing.

Well, I do not want parentheses to randomly hide members. (a ~ b).length

Then it easy to add a comma to create a one element tuple. IE: (a ~ b).length == a.length + b.length (a ~ b,).length == 1

The motivation for having these conversions was syntactic ambiguity. If there is none, just do it like this: (create_tuple,) get_value[0]
Sep 23 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 02:44, Timon Gehr a écrit :
 The motivation for having these conversions was syntactic ambiguity.

 If there is none, just do it like this:

 (create_tuple,)
 get_value[0]

As I answered to Andrei, my motivation isn't related to syntax ambiguity, and I actually don't care what the syntax is as long as it isn't terribly twisted.
Sep 23 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/23/12 7:08 PM, deadalnix wrote:
 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.

The example came from min() applied to built-in "T..." tuples. I've implemented it a few times, and I recall at some point there was a compiler bug related to zero-length tuples. It was rather awkward to address. But there are many other cases. In my opinion, introducing a change of phase at length=1 is just causing trouble. Andrei
Sep 23 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 03:14, Andrei Alexandrescu a écrit :
 On 9/23/12 7:08 PM, deadalnix wrote:
 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.

The example came from min() applied to built-in "T..." tuples. I've implemented it a few times, and I recall at some point there was a compiler bug related to zero-length tuples. It was rather awkward to address. But there are many other cases. In my opinion, introducing a change of phase at length=1 is just causing trouble. Andrei

I understand the trouble here. But why divide and conquer is preferable here over a static foreach over the tuple ? Additionally, what lead me to think the idea is good is how tuple can be used to call function, or to extend the opDispatch feature to templated calls. I didn't wanted to go into such details, because I wanted to test several idea before doing a proposal. A lot of options are possible here, and interaction with other part of the language are everywhere.
Sep 23 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/23/12 9:27 PM, deadalnix wrote:
 I understand the trouble here. But why divide and conquer is preferable
 here over a static foreach over the tuple ?

There are fewer data dependencies, which results on faster execution on today's CPUs. Andrei
Sep 23 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-09-24 07:01, Nick Sabalausky wrote:

 I think one of us is missing something, and I'm not entirely sure
 who.

 As I explained (perhaps poorly), the zero- and one-element tuples *would
 still be* tuples. They would just be implicitly convertible to
 non-tuple form *if* needed, and vice versa. Do you see a reason why
 that would *necessarily* not be the case?

Would that mean you could start doing things like: int a = 3; int b = a[0]; That feels very weird. -- /Jacob Carlborg
Sep 24 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-09-24 12:06, Nick Sabalausky wrote:

 No, because there's nothing typed (int) involved there. But you could do
 this:

      int a = 3;
      (int) b = a;
      a = b;

But you said: "They would just be implicitly convertible to non-tuple form *if* needed, and vice versa." To me that sounds like a tuple of one element of the type int would be implicitly convertible to an int. And, an int would be implicitly convertible to a tuple of one element. -- /Jacob Carlborg
Sep 24 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 14:15, Jacob Carlborg a écrit :
 On 2012-09-24 12:06, Nick Sabalausky wrote:

 No, because there's nothing typed (int) involved there. But you could do
 this:

 int a = 3;
 (int) b = a;
 a = b;

But you said: "They would just be implicitly convertible to non-tuple form *if* needed, and vice versa." To me that sounds like a tuple of one element of the type int would be implicitly convertible to an int. And, an int would be implicitly convertible to a tuple of one element.

I understand your example, but in it, no (int) are involved. So no conversion have to be done (and you get an error). You see in example above that conversion is done when int is given where (int) is expected or vice versa, not whenever the compiler feels to.
Sep 24 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-09-24 15:05, deadalnix wrote:

 I understand your example, but in it, no (int) are involved. So no
 conversion have to be done (and you get an error).

What has that to do with anything. Example: auto a = 3; There's no mention of "int" in that example, yet "a" is still an int.
 You see in example above that conversion is done when int is given where
 (int) is expected or vice versa, not whenever the compiler feels to.

int b = 4; b[0] Why isn't that an example of where a (int) is expected? I'm no expert on how the compiler does semantic analyze but if it sees something like "b[0]" then it thinks: it's either an array, a pointer, an associate array, opAssign or now a tuple. Then it thinks: hey an int is implicitly convertible to a one element tuple, I do that. -- /Jacob Carlborg
Sep 24 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-09-24 22:45, Nick Sabalausky wrote:

 Because 'b' is neither being assigned to an (int) nor passed into a
 template/func parameter that's expecting an (int).

Either I'm just stupid or I've failed completely to understand "implicit convertible to". Another example: struct Foo { int[] arr; alias arr this; } void main () { auto foo = Foo([3, 4]); auto i = foo[0]; } Have a look at the last line. In that line "foo" is implicitly converted to "int[]" with the help of the "alias this" in Foo, because the context requires something "indexable". Since you cannot index a struct the implicit conversion kicks in. What's the difference? -- /Jacob Carlborg
Sep 24 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/24/12 1:01 AM, Nick Sabalausky wrote:
 On Sun, 23 Sep 2012 18:48:22 -0400
 Andrei Alexandrescu<SeeWebsiteForEmail erdani.org>  wrote:
 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.

I think one of us is missing something, and I'm not entirely sure who. As I explained (perhaps poorly), the zero- and one-element tuples *would still be* tuples. They would just be implicitly convertible to non-tuple form *if* needed, and vice versa. Do you see a reason why that would *necessarily* not be the case?

It just creates endless little problems and confusion coming outta the woodwork, as others have pointed out in response to this. There are languages that have also explored a similar approach - a value can be automatically converted to a one-element array and vice versa. It's problematic, especially in a language with generics and function overloading.
 I think it's safe to just not even discuss it.

A nice way to put it :/ Part politician perhaps? ;)

I meant it in a simple and forward way - all I want is to save time and trouble in exploring a no-win design. From sheer experience gathered from years at hacking at this stuff I know this can be done but is not worth the trouble. Since it can be done, there's no argument that would definitively close the discussion, and that demotivates me from coming up with explanations. Andrei
Sep 24 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/24/12 11:23 AM, Eldar Insafutdinov wrote:
 On Monday, 24 September 2012 at 14:52:21 UTC, Steven Schveighoffer wrote:
 Without any research or investigation, what about using a different
 set of delimiters for tuples? Like {1,2,3}

.... and exactly the syntax I was going to propose!

Assume you had this syntax working today. So instead of writing "tuple(a. b. c)" you write "{ a, b, c }". To what extent would your code be better? (Honest question. Don't forget that adding the => syntax for lambda /did/ make for better code.) Andrei
Sep 24 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/24/12 11:29 AM, Andrei Alexandrescu wrote:
 On 9/24/12 11:23 AM, Eldar Insafutdinov wrote:
 On Monday, 24 September 2012 at 14:52:21 UTC, Steven Schveighoffer wrote:
 Without any research or investigation, what about using a different
 set of delimiters for tuples? Like {1,2,3}

.... and exactly the syntax I was going to propose!

Assume you had this syntax working today. So instead of writing "tuple(a. b. c)" you write "{ a, b, c }". To what extent would your code be better? (Honest question. Don't forget that adding the => syntax for lambda /did/ make for better code.) Andrei

Hrm, I meant "tuple(a, b, c)". Andrei
Sep 24 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/24/12 11:47 AM, Steven Schveighoffer wrote:
 On Mon, 24 Sep 2012 11:29:53 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 9/24/12 11:23 AM, Eldar Insafutdinov wrote:
 On Monday, 24 September 2012 at 14:52:21 UTC, Steven Schveighoffer
 wrote:
 Without any research or investigation, what about using a different
 set of delimiters for tuples? Like {1,2,3}

.... and exactly the syntax I was going to propose!

Assume you had this syntax working today. So instead of writing "tuple(a. b. c)" you write "{ a, b, c }". To what extent would your code be better? (Honest question. Don't forget that adding the => syntax for lambda /did/ make for better code.)

I can't honestly say I've used either tuple(a, b, c), or tuples in other languages very much. I can say that I have *avoided* tuples as return values because I don't want to type Tuple!(x, y) as the return type. But I haven't come across that need very much. You can say "yeah, but what about auto?" Cases I'm referring to were to make interface declarations -- can't use auto.

Yah, after writing DIP19 I was like, "creating tuples is nice and easy, but expressing function returns is less so". Besides, the comma operator does not hurt the syntax for types all that much.
 I just wanted to point out that it seems the largest trouble,
 implementation-wise, for DIP19 is the choice of parentheses to denote a
 tuple. If we do want to add built-in tuples, maybe we should be looking
 at a different delimiter.

Indeed. The question is what mileage we get out of it. Andrei
Sep 24 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 17:29, Andrei Alexandrescu a écrit :
 On 9/24/12 11:23 AM, Eldar Insafutdinov wrote:
 On Monday, 24 September 2012 at 14:52:21 UTC, Steven Schveighoffer wrote:
 Without any research or investigation, what about using a different
 set of delimiters for tuples? Like {1,2,3}

.... and exactly the syntax I was going to propose!

Assume you had this syntax working today. So instead of writing "tuple(a. b. c)" you write "{ a, b, c }". To what extent would your code be better? (Honest question. Don't forget that adding the => syntax for lambda /did/ make for better code.) Andrei

{} is often harder to disambiguate then () with current D syntax.
Sep 25 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 01:59, Andrej Mitrovic a écrit :
 On 9/25/12, Steven Schveighoffer<schveiguy yahoo.com>  wrote:
 However, this brings up another issue, what about porting C code?  All of
 a sudden c style casts are no loner errors, but are type tuples!

I think they're still errors: int x = (int)foo; Maybe the compiler could figure out if a cast was attempted rather than a tuple, and could print out the ol' "Can't use C shenanigans in D" error.

It will, because it is trying to parse an expression here, not a declaration.
Sep 25 2012
prev sibling next sibling parent reply "foobar" <foo bar.com> writes:
On Sunday, 23 September 2012 at 21:51:35 UTC, Nick Sabalausky 
wrote:
 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.

 That means (at least I think it means) that things like () or 
 (1) or
 ((1)) require no way to disambiguate between tuple and 
 expression,
 because either way they're the same thing (or at least freely
 convertible).

Nope. One of the ways in math to "build" the positive numbers based on set theory is via singletons: n := |tuple of empty tuples| so "1" is defined as { {} } whereas "0" is simply {}. That does not work with the above suggestion. Now, I realize this is an arguably convoluted math example but it does show that the treating { {} } as {} is limiting the expressive power of tuples.
Sep 24 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/24/12 9:27 AM, Philippe Sigaud wrote:
 On Mon, Sep 24, 2012 at 12:46 PM, Nick Sabalausky
 <SeeWebsiteToContactMe semitwist.com>  wrote:

 That said, I'm not necessarily opposed to the strict separation if we
 had a good candidate for built-in tuple literal syntax. But *if* the
 best we have is parens (and maybe there *is* something better?) then
 maybe this would be an acceptable way to achieve it?

If the problems in DIP 19 are deemed mostly syntactic (1- and 0- element tuples), then maybe *for once* a simple syntax change could solve them? I know syntax proposals are a dime a dozen in this newsgroup, but why not here, to avoid the ((1)) problem? For example choosing { 1, 2} to represent a tuple? { } blocks in D enclose semi-colon terminated declarations or expressions, but here it's enclosing comma-separated expressions. And, since { } is probably dangerous without a completly integrated type systems giving a type to all expressions ( (){} anyone?) , why not use (| 1, 2 |), or whatever syntax strikes our collective fancy? (I propose *not* to use< ,>) Then, the compiler has to change the way it prints its internal tuple, to follow the new syntax.
 Ie:

 // (3) is polysemous: Either int or (int)
 int   a = (3);  // Normal value
 (int) b = (3);  // One-element tuple
 auto  c = (3);  // Default to normal "int"?

For the third case, I'd say it defaults to a tuple. But then again, using another syntax solves this problem. auto c = (| 3 |); // or c = { 3 };

I think my main problem with this is that I'm perfectly happy with the baseline, which has "tuple(" as the left delimiter and ")" as the right delimiter. I'd be more excited to invent notation if there was overwhelming or at least considerable evidence that the notation considerably helps certain use cases, or is very frequent. As things are, I'd be quite "meh" about suddenly adding lenses. Andrei
Sep 24 2012
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-09-24 17:24, Andrei Alexandrescu wrote:

 I think my main problem with this is that I'm perfectly happy with the
 baseline, which has "tuple(" as the left delimiter and ")" as the right
 delimiter. I'd be more excited to invent notation if there was
 overwhelming or at least considerable evidence that the notation
 considerably helps certain use cases, or is very frequent. As things
 are, I'd be quite "meh" about suddenly adding lenses.

Declaring a tuple is still quire verbose can could really benefit from a shorter syntax. (int, int) foo (); Vs import std.typecons; Tuple!(int, int) foo (); // or what the correct syntax is -- /Jacob Carlborg
Sep 24 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 17:24, Andrei Alexandrescu a écrit :
 On 9/24/12 9:27 AM, Philippe Sigaud wrote:
 On Mon, Sep 24, 2012 at 12:46 PM, Nick Sabalausky
 <SeeWebsiteToContactMe semitwist.com> wrote:

 That said, I'm not necessarily opposed to the strict separation if we
 had a good candidate for built-in tuple literal syntax. But *if* the
 best we have is parens (and maybe there *is* something better?) then
 maybe this would be an acceptable way to achieve it?

If the problems in DIP 19 are deemed mostly syntactic (1- and 0- element tuples), then maybe *for once* a simple syntax change could solve them? I know syntax proposals are a dime a dozen in this newsgroup, but why not here, to avoid the ((1)) problem? For example choosing { 1, 2} to represent a tuple? { } blocks in D enclose semi-colon terminated declarations or expressions, but here it's enclosing comma-separated expressions. And, since { } is probably dangerous without a completly integrated type systems giving a type to all expressions ( (){} anyone?) , why not use (| 1, 2 |), or whatever syntax strikes our collective fancy? (I propose *not* to use< ,>) Then, the compiler has to change the way it prints its internal tuple, to follow the new syntax.
 Ie:

 // (3) is polysemous: Either int or (int)
 int a = (3); // Normal value
 (int) b = (3); // One-element tuple
 auto c = (3); // Default to normal "int"?

For the third case, I'd say it defaults to a tuple. But then again, using another syntax solves this problem. auto c = (| 3 |); // or c = { 3 };

I think my main problem with this is that I'm perfectly happy with the baseline, which has "tuple(" as the left delimiter and ")" as the right delimiter. I'd be more excited to invent notation if there was overwhelming or at least considerable evidence that the notation considerably helps certain use cases, or is very frequent. As things are, I'd be quite "meh" about suddenly adding lenses. Andrei

Clearly, crating a tuple that way isn't the issue. tuple() is ok.
Sep 25 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 17:55, Philippe Sigaud a écrit :
 On Mon, Sep 24, 2012 at 5:24 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org>  wrote:
 I think my main problem with this is that I'm perfectly happy with the
 baseline, which has "tuple(" as the left delimiter and ")" as the right
 delimiter.

I found it a bit long compared to other languages in the beginning, but I've been using them heavily since you added them to Phobos and I'm now quite happy with them. I even like the .expand thingy. (I have a few nitpicks, about std.typecons.tuple, but those would be the subject of another thread)
 I'd be more excited to invent notation if there was overwhelming
 or at least considerable evidence that the notation considerably helps
 certain use cases, or is very frequent. As things are, I'd be quite "meh"
 about suddenly adding lenses.

OK. One standard use for tuples is assignment: a,b = someTuple; // a and b already exist in this scope auto (c,d) = someTuple; // creates c and d and similar variations, which Phobos' tuples do not provide.

And the auto flatten stuff is really weird, and sometime get into the way.
Sep 25 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 11:22 PM, Nick Sabalausky wrote:
 On Mon, 24 Sep 2012 15:27:19 +0200
 Philippe Sigaud <philippe.sigaud gmail.com> wrote:

 On Mon, Sep 24, 2012 at 12:46 PM, Nick Sabalausky
 <SeeWebsiteToContactMe semitwist.com> wrote:

 That said, I'm not necessarily opposed to the strict separation if
 we had a good candidate for built-in tuple literal syntax. But *if*
 the best we have is parens (and maybe there *is* something better?)
 then maybe this would be an acceptable way to achieve it?

If the problems in DIP 19 are deemed mostly syntactic (1- and 0- element tuples), then maybe *for once* a simple syntax change could solve them? I know syntax proposals are a dime a dozen in this newsgroup, but why not here, to avoid the ((1)) problem?

I'm all for that. In fact, I was was just about to post the same suggestion. My bikeshed is colored one of these: (:1,2) (|1,2)

At that point you might as well just use import std.typecons : q = tuple, Q = Tuple; Q!(int, int) foo(){ return q(1, 2); } If built-in tuples are not going to look like (1, 2) then imho we might as well leave them out, while still addressing unpacking in the locations bearophile has designated.
 Minimal syntax (one extra character), no ambiguities with anything
 else AFAIK. Looks kinda funny, but so did !() at first and we all got
 used to that.

 For example choosing { 1, 2} to represent a tuple?

I like it, but what about zero-element tuples vs empty code blocks? (Or is it ok because code blocks can't be used inside, or as, expressions?) Also, it may be too easy to accidentally get mixups between one-element tuples and certain one-statement blocks: { foo(); } // Block vs { foo() } // Either a tuple or a forgotten semicolon Not sure if that's a big enough deal, though. ...

q(foo())
Sep 24 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/25/2012 12:28 AM, bearophile wrote:
 Timon Gehr:

 My bikeshed is colored one of these:

 (:1,2)
 (|1,2)

At that point you might as well just use import std.typecons : q = tuple, Q = Tuple; Q!(int, int) foo(){ return q(1, 2); } If built-in tuples are not going to look like (1, 2) then imho we might as well leave them out,

But the banana syntax doesn't look bad: (||) (|1|) (|1, 2|) (|1, 2, 3|) It's short enough,

It's not shorter than q()
 it's not visually noisy,

It adds more noise than q()
 it's simple enough to type,

It is harder to type than q().
 it consistently avoids the problems with literals for
 0-tuples and 1-tuples, and it's sufficiently intuitive once you
 have seen it one time.  It's just a bit longer to type than the
 syntax with simple (), that has problems with the shorter tuples.

I still think any built-in special syntax that differs from (1,2,3) is not worth it.
 The now dead Fortress language used several similar syntaxes,
 like (|...|), {|...|}, [|...|], etc.

Sep 24 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-09-25 03:19, ixid wrote:
 What would a special case where the first level of tuple (with higher
 levels being tuples in tuples) didn't require parens break? This would
 be a beautiful syntax:

 auto a = 1, 2; // A tuple of two ints

 int, string fun(double, double d) {
      return cast(int) (d[0] * d[1]), "hello";
 }

 auto a, b = 1, 2; // Two ints
 auto a = fun(1.0, 1.0); // Tuple of 1 and "hello".
 auto a, b = fun(1.0, 1.0); // An int and a string.

I like this one. -- /Jacob Carlborg
Sep 25 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 17:51, ixid a écrit :
 You've shown it's clearly incompatible with the current language and
 would break lots of code. What would it break if assignment required
 explicit tuple brackets?

 (int a, b) = foo(); // A tuple assignment, 'a' and 'b' will be filled in
 order by the multiple return values of foo()

 int foo() {
 return 1;
 }

 (int a, b) = foo(); // Also valid and only sets 'a'


 int, int foo() {
 return 1, 2;
 }

 int a = foo(); // Also valid and 'a' takes the first tuple value

 (int a, auto b) = foo(); // Evaluated left to right so 'a' will take the
 first argument of foo and b will auto to a type or tuple of what
 remains. It will error if there is nothing remaining for b. This would
 still allow the clean expression of stand-alone tuples and function
 arguments and return values.

 int, string a = 1, "hello";
 int, string foo(double, double a) {
 return cast(int) (d[0] * d[1]), "hello";
 }

WTF are thoses affirmations ?
Sep 25 2012
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, September 25, 2012 22:39:37 ixid wrote:
 I meant to reply to your post rather than Jacob's.

It would help if you actually quoted at least _some_ of the post that you're replying to. I have _no_ idea what post you're replying to here. Many people view this newsgroup without any threading (meaning that your post is _really_ confusing), and even when you _have_ threading, it doesn't always work right (my mail client frequently puts posts in the wrong place in the hierarchy). So, please include at least some of the posts that you're replying to in your replies. - Jonathan M Davis
Sep 25 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/25/12 4:44 PM, Jonathan M Davis wrote:
 On Tuesday, September 25, 2012 22:39:37 ixid wrote:
 I meant to reply to your post rather than Jacob's.

It would help if you actually quoted at least _some_ of the post that you're replying to. I have _no_ idea what post you're replying to here. Many people view this newsgroup without any threading (meaning that your post is _really_ confusing), and even when you _have_ threading, it doesn't always work right (my mail client frequently puts posts in the wrong place in the hierarchy). So, please include at least some of the posts that you're replying to in your replies. - Jonathan M Davis

Also, ixid, while you're at it, don't _forget_ to _underline_ all _words_ that are _important_. _Andrei_
Sep 25 2012
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 03:19, ixid a écrit :
 What would a special case where the first level of tuple (with higher
 levels being tuples in tuples) didn't require parens break? This would
 be a beautiful syntax:

 auto a = 1, 2; // A tuple of two ints

 int, string fun(double, double d) {
 return cast(int) (d[0] * d[1]), "hello";
 }

 auto a, b = 1, 2; // Two ints
 auto a = fun(1.0, 1.0); // Tuple of 1 and "hello".
 auto a, b = fun(1.0, 1.0); // An int and a string.

It can get pretty confusing with , separated declarations : int a, b = 3; or worse : int a, int b = foo(); --> (int a, int b) = foo(); or int a, (int b = foo()); and it gets worse with int a, auto b = foo(); But I do agree that this is really nice in many places.
Sep 25 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-09-25 12:05, deadalnix wrote:

 It can get pretty confusing with , separated declarations :

 int a, b = 3;

I wouldn't complain if that became illegal. -- /Jacob Carlborg
Sep 25 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 13:42, Jacob Carlborg a écrit :
 On 2012-09-25 12:05, deadalnix wrote:

 It can get pretty confusing with , separated declarations :

 int a, b = 3;

I wouldn't complain if that became illegal.

Nor me (it was already confusing and confusable with comma expressions), but a hell lot of code rely on comma declarations.
Sep 25 2012
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 23:34, ixid a écrit :
 You've shown it's clearly incompatible with the current language
 and would break lots of code. What would it break if assignment
 required explicit tuple brackets?

 (int a, b) = foo(); // A tuple assignment, 'a' and 'b' will be
 filled in order by the multiple return values of foo()

 int foo() {
 return 1;
 }

 (int a, b) = foo(); // Also valid and only sets 'a'


 int, int foo() {
 return 1, 2;
 }

 int a = foo(); // Also valid and 'a' takes the first tuple value

 (int a, auto b) = foo(); // Evaluated left to right so 'a' will
 take the first argument of foo and b will auto to a type or tuple
 of what remains. It will error if there is nothing remaining for
 b. This would still allow the clean expression of stand-alone
 tuples and function arguments and return values.

This does not exist according to current language specs, so it is up to us to decide if it works and how.
 int, string a = 1, "hello";
 int, string foo(double, double a) {
 return cast(int) (d[0] * d[1]), "hello";
 }

This is incompatible with current language specs (or will ends up with highly bizantine rules do define what to do, in a topic where it is already complex).
 Doesn't the needs of bracketing to determine order operation,
 (stuff) more or less imply that implicit conversion between one
 member tuples and the type of that tuple member is a requirement?
 Or would the (stuff, ) syntax be better?

I don't know.
Sep 26 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 28/09/2012 00:39, ixid a écrit :
 int, string a = 1, "hello";
 int, string foo(double, double a) {
 return cast(int) (d[0] * d[1]), "hello";
 }

 This is incompatible with current language specs (or will ends up with
 highly bizantine rules do define what to do, in a topic where it is
 already complex).

Which parts exactly are Byzantine and why? I'm not arguing, just interested to know as this is the part that seems most desirable to me.

int a, int b = 3, 3; Considering how the syntax is defined, this will not do what you expect, and it is not fixable easily without breaking other constructs.
Sep 28 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/24/12 6:28 PM, bearophile wrote:
 Timon Gehr:

 My bikeshed is colored one of these:

 (:1,2)
 (|1,2)

At that point you might as well just use import std.typecons : q = tuple, Q = Tuple; Q!(int, int) foo(){ return q(1, 2); } If built-in tuples are not going to look like (1, 2) then imho we might as well leave them out,

But the banana syntax doesn't look bad: (||) (|1|) (|1, 2|) (|1, 2, 3|)

tuple() tuple(1) tuple(1, 2) tuple(1, 2, 3) also arguably enjoys the same advantages and in fact is much more intuitive. Like, totally intuitive. Like, it says "tuple" to create a tuple. And one advantage is, there's never ever going to be butt jokes about tuple() as there'd be with "(||)".
 It's short enough, it's not visually noisy, it's simple enough to
 type, it consistently avoids the problems with literals for
 0-tuples and 1-tuples, and it's sufficiently intuitive once you
 have seen it one time. It's just a bit longer to type than the
 syntax with simple (), that has problems with the shorter tuples.

 The now dead Fortress language used several similar syntaxes,
 like (|...|), {|...|}, [|...|], etc.

Well let's not take inspiration from dead languages :o). Andrei
Sep 24 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 01:39, Andrei Alexandrescu a écrit :
 On 9/24/12 6:28 PM, bearophile wrote:
 Timon Gehr:

 My bikeshed is colored one of these:

 (:1,2)
 (|1,2)

At that point you might as well just use import std.typecons : q = tuple, Q = Tuple; Q!(int, int) foo(){ return q(1, 2); } If built-in tuples are not going to look like (1, 2) then imho we might as well leave them out,

But the banana syntax doesn't look bad: (||) (|1|) (|1, 2|) (|1, 2, 3|)

tuple() tuple(1) tuple(1, 2) tuple(1, 2, 3) also arguably enjoys the same advantages and in fact is much more intuitive. Like, totally intuitive. Like, it says "tuple" to create a tuple. And one advantage is, there's never ever going to be butt jokes about tuple() as there'd be with "(||)".

The problem with tuple() isn't its syntax, but what you can or can't do with the resulting tuple.
Sep 25 2012
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-09-25 00:28, bearophile wrote:

 (||)
 (|1|)
 (|1, 2|)
 (|1, 2, 3|)

What about: || |1| |1, 2| -- /Jacob Carlborg
Sep 25 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 09:11, Jacob Carlborg a écrit :
 On 2012-09-25 00:28, bearophile wrote:

 (||)
 (|1|)
 (|1, 2|)
 (|1, 2, 3|)

What about: || |1| |1, 2|

Yeah and why not þ1, 2þ or ŀ1, 2ŀ ? maybe ↓1, 2↓ is better ? or « 1, 2 » (this one at least is readable).
Sep 25 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 22:55, Nick Sabalausky a écrit :
 On Tue, 25 Sep 2012 12:07:33 +0200
 deadalnix<deadalnix gmail.com>  wrote:

 Le 25/09/2012 09:11, Jacob Carlborg a écrit :
 On 2012-09-25 00:28, bearophile wrote:

 (||)
 (|1|)
 (|1, 2|)
 (|1, 2, 3|)

What about: || |1| |1, 2|

Yeah and why not þ1, 2þ or ŀ1, 2ŀ ? maybe ↓1, 2↓ is better ? or « 1, 2 » (this one at least is readable).

| is easily typed. þ, ŀ, ↓ and « I had to copy-paste.

My post was ironic. It isn't hard to come up with new characters but is it really useful ?
Sep 26 2012
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 16:59, foobar a écrit :
 I'm a bit confused about what is specifically proposed here:
 - Is the suggestion to limit tuples to >1 elements? *This* I'm against
 for practical as well as completeness reasons. Andrei already provided
 one example, and another would be a proper unit type. e.g.
 void foo(int a) {}
 void bar (int b) { return foo(b); }
 - Is the suggestion to allow implicit conversion between (T) and T?
 This brings almost no benefit - (you save two keystrokes?) and adds a
 special case to the language. The added complexity really does not
 justify this.

In fact, they don't need to unpack only for 1 element tuples, but this is the tricky case. Today, tuples auto unpack on function call for instance : auto t = tuple (1, 2); foo(t); // call foo(int, int)
Sep 25 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/25/12 6:10 AM, deadalnix wrote:
 Le 24/09/2012 16:59, foobar a écrit :
 I'm a bit confused about what is specifically proposed here:
 - Is the suggestion to limit tuples to >1 elements? *This* I'm against
 for practical as well as completeness reasons. Andrei already provided
 one example, and another would be a proper unit type. e.g.
 void foo(int a) {}
 void bar (int b) { return foo(b); }
 - Is the suggestion to allow implicit conversion between (T) and T?
 This brings almost no benefit - (you save two keystrokes?) and adds a
 special case to the language. The added complexity really does not
 justify this.

In fact, they don't need to unpack only for 1 element tuples, but this is the tricky case. Today, tuples auto unpack on function call for instance : auto t = tuple (1, 2); foo(t); // call foo(int, int)

Actually that's not the case. You need to write foo(t.expand); (and I think that's a good thing). Andrei
Sep 25 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 15:38, Andrei Alexandrescu a écrit :
 On 9/25/12 6:10 AM, deadalnix wrote:
 Le 24/09/2012 16:59, foobar a écrit :
 I'm a bit confused about what is specifically proposed here:
 - Is the suggestion to limit tuples to >1 elements? *This* I'm against
 for practical as well as completeness reasons. Andrei already provided
 one example, and another would be a proper unit type. e.g.
 void foo(int a) {}
 void bar (int b) { return foo(b); }
 - Is the suggestion to allow implicit conversion between (T) and T?
 This brings almost no benefit - (you save two keystrokes?) and adds a
 special case to the language. The added complexity really does not
 justify this.

In fact, they don't need to unpack only for 1 element tuples, but this is the tricky case. Today, tuples auto unpack on function call for instance : auto t = tuple (1, 2); foo(t); // call foo(int, int)

Actually that's not the case. You need to write foo(t.expand); (and I think that's a good thing). Andrei

OK, my bad. It means that tuple(...) behave differently than T... defined tuples. And both behave differently than Caml or Haskell's tuples. isn't the time for some unification ? Preferably on how tuples work in other languages, except if limitations can be shown and better proposal are made (and not include that in D2.xxx).
Sep 25 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/25/12 10:05 AM, deadalnix wrote:
 OK, my bad. It means that tuple(...) behave differently than T...
 defined tuples.

 And both behave differently than Caml or Haskell's tuples.

 isn't the time for some unification ? Preferably on how tuples work in
 other languages, except if limitations can be shown and better proposal
 are made (and not include that in D2.xxx).

I'm not sure actually. The way I look at it, built-in tuples are quite low-level (types can't be spelled, automatic expansion and flattening, undecided first-class semantics) and should seldom be dealt with directly. The best use of built-in tuples is in the implementation of truly well-behaved, composable tuples. Andrei
Sep 25 2012
next sibling parent reply Michel Fortin <michel.fortin michelf.ca> writes:
On 2012-09-25 15:08:25 +0000, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 On 9/25/12 10:05 AM, deadalnix wrote:
 OK, my bad. It means that tuple(...) behave differently than T...
 defined tuples.
 
 And both behave differently than Caml or Haskell's tuples.
 
 isn't the time for some unification ? Preferably on how tuples work in
 other languages, except if limitations can be shown and better proposal
 are made (and not include that in D2.xxx).

I'm not sure actually. The way I look at it, built-in tuples are quite low-level (types can't be spelled, automatic expansion and flattening, undecided first-class semantics) and should seldom be dealt with directly. The best use of built-in tuples is in the implementation of truly well-behaved, composable tuples.

The built-in tuple is also quite useful when defining templates. In essence, we have two kinds of tuples: the built-in language tuple is the "unpacked" tuple while Phobos hosts the "packed" one. They each have their own use case and they can coexist peacefully. But the language itself needs to standardize on one or the other. Take this example (which is currently illegal because you can't return a built-in language tuple): T getThings(T...)(T t) { return t; } In this situation, there is no question that the language tuple needs to work as an expanded tuple inside the parameter list of getTuple. But now, what is the return type of makeTuple(1,2)? Obviously it's (int, int), but is (int, int) the same thing as T? Or is it a packed version of T? Well, it can't be a packed version of T because T is already the unpacked type. I mean that for a function that simply return its argument this should work: T t1; T t2 = getThings!(T)(t1); If the language made T… a packed tuple instead, then we could use the packed tuple everywhere and unpack it where necessary, and something like this could be used to make a packed tuple: T getThings(T...)(T.expand t) { return T(t); } T t1; T t2 = getThings!(T)(t1.expand); But we can't have it both ways, and the above would be a very drastic change to the language. I'm of the opinion that if we want to add tuple returns to the language (which I'd certainly like), it'll have to be the same kind of tuple we currently have built-in in the language: the auto-expanding kind. As we all know, this doesn't preclude anyone from building packed library tuples: Tuple!(T) getThings(T...)(T t) { return tuple(t); } Although to make things less confusing, I think the built-in language tuple should give up its name. It could become a "sequence". Renaming the built-in one would certainly be less trouble, as code doesn't refer to it by its name, and you can pick a name that fits better with its auto-expanding behaviour. -- Michel Fortin michel.fortin michelf.ca http://michelf.ca/
Sep 25 2012
next sibling parent reply Michel Fortin <michel.fortin michelf.ca> writes:
On 2012-09-25 16:38:31 +0000, "Jonathan M Davis" <jmdavisProg gmx.com> said:

 On Tuesday, September 25, 2012 12:28:15 Michel Fortin wrote:
 Although to make things less confusing, I think the built-in language
 tuple should give up its name. It could become a "sequence". Renaming
 the built-in one would certainly be less trouble, as code doesn't refer
 to it by its name, and you can pick a name that fits better with its
 auto-expanding behaviour.

Not referred to by its name? It's refereed to as TypeTuple all over the place. It's arguably a bad name, but it would break a _lot_ of code to change it now.

TypeTuple is only a construct allowing you to create a built-in language tuple, one that does not necessarily match the definition of a TypeTuple. The language spec defines a Tuples, TypeTuples, and ExpressionTuples with these words (ironically, using the word "sequence" twice): """ If the last template parameter in the TemplateParameterList is declared as a TemplateTupleParameter, it is a match with any trailing template arguments. The sequence of arguments form a Tuple. A Tuple is not a type, an expression, or a symbol. It is a sequence of any mix of types, expressions or symbols. A Tuple whose elements consist entirely of types is called a TypeTuple. A Tuple whose elements consist entirely of expressions is called an ExpressionTuple. """ Source: http://dlang.org/template.html -- Michel Fortin michel.fortin michelf.ca http://michelf.ca/
Sep 25 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/25/2012 09:29 PM, kenji hara wrote:
 2012/9/26 Jonathan M Davis <jmdavisProg gmx.com>:
 On Tuesday, September 25, 2012 13:45:50 Michel Fortin wrote:
 On 2012-09-25 16:38:31 +0000, "Jonathan M Davis" <jmdavisProg gmx.com> said:
 On Tuesday, September 25, 2012 12:28:15 Michel Fortin wrote:
 Although to make things less confusing, I think the built-in language
 tuple should give up its name. It could become a "sequence". Renaming
 the built-in one would certainly be less trouble, as code doesn't refer
 to it by its name, and you can pick a name that fits better with its
 auto-expanding behaviour.

Not referred to by its name? It's refereed to as TypeTuple all over the place. It's arguably a bad name, but it would break a _lot_ of code to change it now.

language tuple, one that does not necessarily match the definition of a TypeTuple. The language spec defines a Tuples, TypeTuples, and ExpressionTuples with these words (ironically, using the word "sequence" twice): """ If the last template parameter in the TemplateParameterList is declared as a TemplateTupleParameter, it is a match with any trailing template arguments. The sequence of arguments form a Tuple. A Tuple is not a type, an expression, or a symbol. It is a sequence of any mix of types, expressions or symbols. A Tuple whose elements consist entirely of types is called a TypeTuple. A Tuple whose elements consist entirely of expressions is called an ExpressionTuple. """ Source: http://dlang.org/template.html

I wasn't aware of that being in the language definition, but it doesn't change the fact that they're used and referred to in code as TypeTuple, and renaming that would break a lot of code. And it _is_ used for the built-in tuple type, regardless of whether the spec considers the terms type tuple and expression tuple to refer to distinct entities. Rename the stuff in the spec to whatever you like, but the library uses the term TypeTuple, so it _is_ used in code. - Jonathan M Davis

I like current design - open (built-in, automatically flattened, and *unpacked*) tuple, and closed (library, be structured, and *packed*) tuple. But, the two are often confused, by the word "tuple". It has introduced badly confusion in many discussions. To make matters worse, it had often invoked incorrect suggestion that merging the two into one. My suggestion is very simple. 1. Change all words "built-in tuple" in the documentation to "built-in sequence". Then, in the D language world, we can have clarify name for the built-in one. 2. Introduce new templates, Seq, TypeSeq, and ExpSeq. template Seq(T...) { alias T Seq; } // identical with std.typetuple.TypeTuple

+1.
      template TypeSeq(T...) if (allSatisfy!(isType, T)) { alias T TypeSeq; }
      template ExpSeq(T...) if (allSatisfy!(isExpression, T)) { alias T ExpSeq;
}

    If you really want to a sequence with heterogeneous elements, use
 Seq template. Otherwise use TypeSeq or ExpSeq based on your purpose.

 Kenji Hara

I don't consider TypeSeq and ExpSeq crucial. They do not do more checking than using the sequence in type or expression context triggers automatically, but YMMV.
Sep 25 2012
prev sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 25-Sep-12 23:29, kenji hara wrote:
 My suggestion is very simple.
 1. Change all words "built-in tuple" in the documentation to "built-in
 sequence". Then, in the D language world, we can have clarify name for
 the built-in one.
 2. Introduce new templates, Seq, TypeSeq, and ExpSeq.

      template Seq(T...) { alias T Seq; }    // identical with
 std.typetuple.TypeTuple
      template TypeSeq(T...) if (allSatisfy!(isType, T)) { alias T TypeSeq; }
      template ExpSeq(T...) if (allSatisfy!(isExpression, T)) { alias T ExpSeq;
}

    If you really want to a sequence with heterogeneous elements, use
 Seq template. Otherwise use TypeSeq or ExpSeq based on your purpose.

-- Dmitry Olshansky
Sep 26 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/25/12 3:39 PM, ixid wrote:
 "T.expand" naturally has the connotation "unpacked" to me,

Isn't T.unpack clearer? Does that clash with a different usage for the term?

Let's stay with .expand. Andrei
Sep 25 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, September 25, 2012 12:28:15 Michel Fortin wrote:
 Although to make things less confusing, I think the built-in language
 tuple should give up its name. It could become a "sequence". Renaming
 the built-in one would certainly be less trouble, as code doesn't refer
 to it by its name, and you can pick a name that fits better with its
 auto-expanding behaviour.

Not referred to by its name? It's refereed to as TypeTuple all over the place. It's arguably a bad name, but it would break a _lot_ of code to change it now. - Jonathan M Davis
Sep 25 2012
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 25/09/2012 17:08, Andrei Alexandrescu a écrit :
 On 9/25/12 10:05 AM, deadalnix wrote:
 OK, my bad. It means that tuple(...) behave differently than T...
 defined tuples.

 And both behave differently than Caml or Haskell's tuples.

 isn't the time for some unification ? Preferably on how tuples work in
 other languages, except if limitations can be shown and better proposal
 are made (and not include that in D2.xxx).

I'm not sure actually. The way I look at it, built-in tuples are quite low-level (types can't be spelled, automatic expansion and flattening, undecided first-class semantics) and should seldom be dealt with directly. The best use of built-in tuples is in the implementation of truly well-behaved, composable tuples. Andrei

We currently have 2 type of tuples, both unsatisfying it its own way (and I assume I can say it is confusing as I was confused before). If the new tuple stuff is implemented, D will ends up with 3 tuples systems, 2 of them unsatisfying and 1 of them satisfying (at least that is the goal). Still, language complexity would have increased in the process and have 3 time the same feature with different flavor isn't a good thing. Even if the tuple solution is a good one, the resulting situation isn't.
Sep 25 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/25/12 4:37 PM, deadalnix wrote:
 Le 25/09/2012 17:08, Andrei Alexandrescu a écrit :
 On 9/25/12 10:05 AM, deadalnix wrote:
 OK, my bad. It means that tuple(...) behave differently than T...
 defined tuples.

 And both behave differently than Caml or Haskell's tuples.

 isn't the time for some unification ? Preferably on how tuples work in
 other languages, except if limitations can be shown and better proposal
 are made (and not include that in D2.xxx).

I'm not sure actually. The way I look at it, built-in tuples are quite low-level (types can't be spelled, automatic expansion and flattening, undecided first-class semantics) and should seldom be dealt with directly. The best use of built-in tuples is in the implementation of truly well-behaved, composable tuples. Andrei

We currently have 2 type of tuples, both unsatisfying it its own way (and I assume I can say it is confusing as I was confused before). If the new tuple stuff is implemented, D will ends up with 3 tuples systems, 2 of them unsatisfying and 1 of them satisfying (at least that is the goal). Still, language complexity would have increased in the process and have 3 time the same feature with different flavor isn't a good thing. Even if the tuple solution is a good one, the resulting situation isn't.

I agree. That's why I want to take the minimum amount of steps to make library tuples work. That minimum amount may be 1, i.e. just implement deconstruction. Andrei
Sep 25 2012
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, September 26, 2012 21:54:44 foobar wrote:
 Library tuples have broken semantics.
 Tuples supposed to have _structural_ typing which AFAIK can only
 be correctly implemented in language.
 
 import std.typecons.TypeTuple;
 
 struct MyTuple(T...)() {}
 
 auto libTup = tuple(123, "hello");
 MyTuple myTup = libTup; // broken
 
 This is broken cause structs in D are nominally typed and even
 though both pack the same inner-types, they are not equal.

Of course, they're not equal. One is a Tuple and one is a MyTuple. Why on earth would you expect them to be considered equal? Just use Tuple. And it's not like you'd be using MyTuple if tuples were built-in. You'd just use the built-in tuples. So, this really makes no sense to me at all. - Jonathan M Davis
Sep 26 2012
next sibling parent Piotr Szturmaj <bncrbme jadamspam.pl> writes:
Jonathan M Davis wrote:
 It sounds to me like the reason that structural typing is needed is because
 Tuple allows you to name its fields, which I've always thought was a bad idea,
 and which a built-in tuple definitely wouldn't do. If you couldn't name its
 fields, then any Tuple containing the same sequence of types would be the same
 type. So, the problem is caused by a feature that built-in tuples wouldn't
 even have.

Exactly my PoV. I think that "tuples with named fields" should be anonymous structs and pure tuples shouldn't have named fields.
Sep 26 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-09-27 00:38, bearophile wrote:

 I have appreciated named fields of D tuples since the beginning, I have
 found them quite handy. With them sometimes you don't need to unpack a
 tuple, you can just access its fields with a nice name, avoiding to move
 around more than one variable.

If you could do something like this: auto x, y = tuple(1, 2); Wouldn't that be an acceptable solution instead? -- /Jacob Carlborg
Sep 26 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 27/09/2012 08:17, Jacob Carlborg a écrit :
 On 2012-09-27 00:38, bearophile wrote:

 I have appreciated named fields of D tuples since the beginning, I have
 found them quite handy. With them sometimes you don't need to unpack a
 tuple, you can just access its fields with a nice name, avoiding to move
 around more than one variable.

If you could do something like this: auto x, y = tuple(1, 2); Wouldn't that be an acceptable solution instead?

It is ambiguous with the comma declaration syntax.
Sep 28 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-09-28 19:09, deadalnix wrote:

 It is ambiguous with the comma declaration syntax.

I could live without it. -- /Jacob Carlborg
Sep 29 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 27/09/2012 00:38, bearophile a écrit :
 Jonathan M Davis:

 It sounds to me like the reason that structural typing is needed is
 because
 Tuple allows you to name its fields, which I've always thought was a
 bad idea,

I have appreciated named fields of D tuples since the beginning, I have found them quite handy. With them sometimes you don't need to unpack a tuple, you can just access its fields with a nice name, avoiding to move around more than one variable.

What is the benefit over a struct ?
Sep 28 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 So, this really makes no sense to me at all.

I agree that foobar examples aren't so good. But it's true that Tuple() gives some troubles regarding missed structural typing: import std.stdio, std.typecons; alias Tuple!(float, float) T1; alias Tuple!(float,"x", float,"y") T2; void foo1(T1 t) {} void foo2(T2 t) {} void main() { auto p1 = T1(1, 2); auto p2 = T2(1, 2); p1 = p2; // no error p2 = p1; // no error T1[] a1; T2[] a2; a1 = a2; // error a2 = a1; // error foo1(p2); // error foo2(p1); // error } Generally I think "p1 = p2;" is OK, while "p2 = p1;" is not so good, because T2 is more specialized. There are several other more or less similar things related to structural typing of tuples that a well implemented built-in tuple type will need to address. In Bugzilla there are some open bug reports on similar matters. Fixing them with the library defined tuples is hard. Of course a possible solution is to improve the D language to allow the programmer to specify very good struct-based tuples in library code, I think but doing this is more complex than implementing good built-in tuples. Bye, bearophile
Sep 26 2012
prev sibling parent "foobar" <foo bar.com> writes:
On Wednesday, 26 September 2012 at 21:31:13 UTC, Jonathan M Davis 
wrote:
 On Wednesday, September 26, 2012 21:54:44 foobar wrote:
 Library tuples have broken semantics.
 Tuples supposed to have _structural_ typing which AFAIK can 
 only
 be correctly implemented in language.
 
 import std.typecons.TypeTuple;
 
 struct MyTuple(T...)() {}
 
 auto libTup = tuple(123, "hello");
 MyTuple myTup = libTup; // broken
 
 This is broken cause structs in D are nominally typed and even
 though both pack the same inner-types, they are not equal.

Of course, they're not equal. One is a Tuple and one is a MyTuple. Why on earth would you expect them to be considered equal? Just use Tuple. And it's not like you'd be using MyTuple if tuples were built-in. You'd just use the built-in tuples. So, this really makes no sense to me at all. - Jonathan M Davis

I do _not_ want to consider two different _structs_ (nominal types) as the same type. I would like to get correct tuple semantics which means _structural_ typing (I thought I emphasized that enough in the OP). A tuple is defined by its contained types, *not* its name. D is a systems language - there are at least half a dozen posts on this NG where people implemented their own runtime/standard libraries for their own purposes. There is at least one OS kernel project that I know of written in D, also with its own specialized libs. And of course we should not forget the embedded hardware space. All provide ample opportunity for exactly the same scenario as in my example - provide a competing, non compatible, tuple implementations and voilà, you just got an incompatibility in the language. Two ways to prevent this, 1. add support in D in order to allow defining a library tuple type _with_ _correct_ _structural_ typing. 2. make tuples a language construct.
Sep 27 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Mon, Sep 24, 2012 at 12:46 PM, Nick Sabalausky
<SeeWebsiteToContactMe semitwist.com> wrote:

 That said, I'm not necessarily opposed to the strict separation if we
 had a good candidate for built-in tuple literal syntax. But *if* the
 best we have is parens (and maybe there *is* something better?) then
 maybe this would be an acceptable way to achieve it?

If the problems in DIP 19 are deemed mostly syntactic (1- and 0- element tuples), then maybe *for once* a simple syntax change could solve them? I know syntax proposals are a dime a dozen in this newsgroup, but why not here, to avoid the ((1)) problem? For example choosing { 1, 2} to represent a tuple? { } blocks in D enclose semi-colon terminated declarations or expressions, but here it's enclosing comma-separated expressions. And, since { } is probably dangerous without a completly integrated type systems giving a type to all expressions ( (){} anyone?) , why not use (| 1, 2 |), or whatever syntax strikes our collective fancy? (I propose *not* to use < , >) Then, the compiler has to change the way it prints its internal tuple, to follow the new syntax.
 Ie:

 // (3) is polysemous: Either int or (int)
 int   a = (3);  // Normal value
 (int) b = (3);  // One-element tuple
 auto  c = (3);  // Default to normal "int"?

For the third case, I'd say it defaults to a tuple. But then again, using another syntax solves this problem. auto c = (| 3 |); // or c = { 3 };
Sep 24 2012
prev sibling next sibling parent "foobar" <foo bar.com> writes:
On Monday, 24 September 2012 at 10:05:18 UTC, Nick Sabalausky 
wrote:
 On Mon, 24 Sep 2012 10:56:40 +0200
 Jacob Carlborg <doob me.com> wrote:

 On 2012-09-24 07:01, Nick Sabalausky wrote:
 
 I think one of us is missing something, and I'm not entirely 
 sure
 who.

 As I explained (perhaps poorly), the zero- and one-element 
 tuples
 *would still be* tuples. They would just be implicitly 
 convertible
 to non-tuple form *if* needed, and vice versa. Do you see a 
 reason
 why that would *necessarily* not be the case?

Would that mean you could start doing things like: int a = 3; int b = a[0]; That feels very weird.

No, because there's nothing typed (int) involved there. But you could do this: int a = 3; (int) b = a; a = b; Or this: void foo((int) a) { int b1 = a[0]; int b2 = a; } int c = 3; foo(c);

What's the point than? here's equivalent code without this "feature": int a = 3; (int) b = (a); // explicitly make 1-tuple (a) = b; // unpacking syntax void foo((int) a) { int b1 = a[0]; (int b2) = a; // one possible syntax } int c = 3; foo ((c));
Sep 24 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 Sep 2012 01:01:29 -0400, Nick Sabalausky  
<SeeWebsiteToContactMe semitwist.com> wrote:

 On Sun, 23 Sep 2012 18:48:22 -0400
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 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.

I think one of us is missing something, and I'm not entirely sure who. As I explained (perhaps poorly), the zero- and one-element tuples *would still be* tuples. They would just be implicitly convertible to non-tuple form *if* needed, and vice versa. Do you see a reason why that would *necessarily* not be the case?

(int[]) x; int a = x.length; is a == 0 or 1? I agree with Andrei, we need something different. I don't profess to be even close to an expert on tuples, but I feel they should be built-in to the language, since they are actually language constructs that we are declaring types for. Without any research or investigation, what about using a different set of delimiters for tuples? Like {1,2,3} Right now, I think that is reserved for static struct initializers. But can't those be considered a tuple also? Someone will probably destroy this 10 milliseconds after I send it :) -Steve
Sep 24 2012
prev sibling next sibling parent "foobar" <foo bar.com> writes:
On Monday, 24 September 2012 at 10:20:01 UTC, Nick Sabalausky 
wrote:
 On Mon, 24 Sep 2012 10:47:38 +0200
 "foobar" <foo bar.com> wrote:
 
 Nope.
 One of the ways in math to "build" the positive numbers based 
 on set theory is via singletons:
 n := |tuple of empty tuples|
 so "1" is defined as { {} } whereas "0" is simply {}. That 
 does not work with the above suggestion. Now, I realize this 
 is an arguably convoluted math example but it does show that 
 the treating { {} } as {} is limiting the expressive power of 
 tuples.
 

And int's are limiting compared to mathematical integers. So what? So ok, maybe this is limiting from a theoretical standpoint. But practically speaking? I dunno. We're not making tuples to emulate set theory here, we're just looking for ad-hoc anonymous structs. Besides, I only said they were logically the same thing, not mechanically. I'm only suggesting that a one-element tuple be implicitly convertible to/from the type of its element. So there would likely still be the different types, it just makes sense that you should be able to use one as the other.

I'm a bit confused about what is specifically proposed here: - Is the suggestion to limit tuples to >1 elements? *This* I'm against for practical as well as completeness reasons. Andrei already provided one example, and another would be a proper unit type. e.g. void foo(int a) {} void bar (int b) { return foo(b); } - Is the suggestion to allow implicit conversion between (T) and T? This brings almost no benefit - (you save two keystrokes?) and adds a special case to the language. The added complexity really does not justify this.
Sep 24 2012
prev sibling next sibling parent "Eldar Insafutdinov" <e.insafutdinov gmail.com> writes:
On Monday, 24 September 2012 at 14:52:21 UTC, Steven 
Schveighoffer wrote:
 (int[]) x;

 int a = x.length;

 is a == 0 or 1?

 I agree with Andrei, we need something different.

This is exactly the question I was going to ask ...
 I don't profess to be even close to an expert on tuples, but I 
 feel they should be built-in to the language, since they are 
 actually language constructs that we are declaring types for.

 Without any research or investigation, what about using a 
 different set of delimiters for tuples?  Like {1,2,3}

... and exactly the syntax I was going to propose! {} is already used in C languages for heterogeneous data structures(structs/classes, JSON etc). Using () creates too many special cases, especially in generic programming and seeing how other languages are dealing with them we'd rather avoid them from the very beginning.
 Right now, I think that is reserved for static struct 
 initializers.  But can't those be considered a tuple also?  
 Someone will probably destroy this 10 milliseconds after I send 
 it :)

 -Steve

It would be awesome if we could make tuples generic initializers for various data types in D. Not just structs but for instance arrays: int[] a = {1, 2, 3, 4}; Compiler possesses enough type information to know that this tuple could be converted to the int[]. P.S. The only collision I see with {} is a delegate literal, but to be honest it's not worth the merit and quite confusing in fact. There are 3 other ways to define a delegate in D which will cover all of the user's needs.
Sep 24 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 Sep 2012 11:29:53 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 9/24/12 11:23 AM, Eldar Insafutdinov wrote:
 On Monday, 24 September 2012 at 14:52:21 UTC, Steven Schveighoffer  
 wrote:
 Without any research or investigation, what about using a different
 set of delimiters for tuples? Like {1,2,3}

.... and exactly the syntax I was going to propose!

Assume you had this syntax working today. So instead of writing "tuple(a. b. c)" you write "{ a, b, c }". To what extent would your code be better? (Honest question. Don't forget that adding the => syntax for lambda /did/ make for better code.)

I can't honestly say I've used either tuple(a, b, c), or tuples in other languages very much. I can say that I have *avoided* tuples as return values because I don't want to type Tuple!(x, y) as the return type. But I haven't come across that need very much. You can say "yeah, but what about auto?" Cases I'm referring to were to make interface declarations -- can't use auto. I can similarly say I have never had need to type x, b (i.e. use the current comma operator), except in for statements. I'd be fine with getting rid of comma operator and not doing tuples in the language, but it certainly feels weird that we have a tuple type in the language, without any formal type unless you alias it (as std.tuple does). It's almost like instead of saying: int[] x; you had to do: typeof([1,2]) x; Yeah, with alias, we could arrive at: Array!int x; But really, it seems odd that the language has a type for something that doesn't have a name/syntax. Odd, but not unworkable. I just wanted to point out that it seems the largest trouble, implementation-wise, for DIP19 is the choice of parentheses to denote a tuple. If we do want to add built-in tuples, maybe we should be looking at a different delimiter. -Steve
Sep 24 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 Sep 2012 11:47:09 -0400, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:


 I can say that I have *avoided* tuples as return values because I don't  
 want to type Tuple!(x, y) as the return type.  But I haven't come across  
 that need very much.  You can say "yeah, but what about auto?"  Cases  
 I'm referring to were to make interface declarations -- can't use auto.

To further this, I would love to see something where "quick POD structs" can be constructed using some builtin syntax. For example: {valid:bool, value:int} which would be equivalent to Tuple!(bool, "valid", int, "value") I would *definitely* like to see that. This might be on par with the => addition. -Steve
Sep 24 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Mon, Sep 24, 2012 at 5:24 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 I think my main problem with this is that I'm perfectly happy with the
 baseline, which has "tuple(" as the left delimiter and ")" as the right
 delimiter.

I found it a bit long compared to other languages in the beginning, but I've been using them heavily since you added them to Phobos and I'm now quite happy with them. I even like the .expand thingy. (I have a few nitpicks, about std.typecons.tuple, but those would be the subject of another thread)
 I'd be more excited to invent notation if there was overwhelming
 or at least considerable evidence that the notation considerably helps
 certain use cases, or is very frequent. As things are, I'd be quite "meh"
 about suddenly adding lenses.

OK. One standard use for tuples is assignment: a,b = someTuple; // a and b already exist in this scope auto (c,d) = someTuple; // creates c and d and similar variations, which Phobos' tuples do not provide.
Sep 24 2012
prev sibling next sibling parent "ponce" <spam spam.org> writes:
On Sunday, 23 September 2012 at 21:51:35 UTC, Nick Sabalausky
wrote:
 *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.

It's informative to look a bit at the Ocaml language: - no distinction between 1-tuple and single value: # 1;; - : int = 1 # (1);; - : int = 1 - "void" type is called unit and its notation is the empty tuple: # ();; - : unit = () - for some reason tuples can't be indexed in Ocaml
Sep 24 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Timon Gehr:

 My bikeshed is colored one of these:

 (:1,2)
 (|1,2)

At that point you might as well just use import std.typecons : q = tuple, Q = Tuple; Q!(int, int) foo(){ return q(1, 2); } If built-in tuples are not going to look like (1, 2) then imho we might as well leave them out,

But the banana syntax doesn't look bad: (||) (|1|) (|1, 2|) (|1, 2, 3|) It's short enough, it's not visually noisy, it's simple enough to type, it consistently avoids the problems with literals for 0-tuples and 1-tuples, and it's sufficiently intuitive once you have seen it one time. It's just a bit longer to type than the syntax with simple (), that has problems with the shorter tuples. The now dead Fortress language used several similar syntaxes, like (|...|), {|...|}, [|...|], etc. Bye, bearophile
Sep 24 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 24 Sep 2012 17:31:27 -0400, Nick Sabalausky  
<SeeWebsiteToContactMe semitwist.com> wrote:

 On Mon, 24 Sep 2012 10:53:14 -0400
 "Steven Schveighoffer" <schveiguy yahoo.com> wrote:
 (int[]) x;

 int a = x.length;

 is a == 0 or 1?

It'd be 1, but I agree that's a pretty solid counter-argument.

It would be if it were valid code :) d complains (and rightly so) that you can't use C-style casts anymore! This is what I really meant: int[] x; int a = (x).length; But I think you got the point. However, this brings up another issue, what about porting C code? All of a sudden c style casts are no loner errors, but are type tuples! -Steve
Sep 24 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/25/12, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 However, this brings up another issue, what about porting C code?  All of
 a sudden c style casts are no loner errors, but are type tuples!

I think they're still errors: int x = (int)foo; Maybe the compiler could figure out if a cast was attempted rather than a tuple, and could print out the ol' "Can't use C shenanigans in D" error.
Sep 24 2012
prev sibling next sibling parent "ixid" <nuaccount gmail.com> writes:
What would a special case where the first level of tuple (with 
higher levels being tuples in tuples) didn't require parens 
break? This would be a beautiful syntax:

auto a = 1, 2; // A tuple of two ints

int, string fun(double, double d) {
     return cast(int) (d[0] * d[1]), "hello";
}

auto a, b = 1, 2; // Two ints
auto a = fun(1.0, 1.0); // Tuple of 1 and "hello".
auto a, b = fun(1.0, 1.0); // An int and a string.
Sep 24 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
deadalnix:

 The problem with tuple() isn't its syntax, but what you can or 
 can't do with the resulting tuple.

See my first posts in this thread :-) On the other hand it's handy to have a compact syntax for something you use often (http://en.wikipedia.org/wiki/Zipf%27s_law ). Bye, bearophile
Sep 25 2012
prev sibling next sibling parent "ixid" <nuaccount gmail.com> writes:
You've shown it's clearly incompatible with the current language 
and would break lots of code. What would it break if assignment 
required explicit tuple brackets?

(int a, b) = foo(); // A tuple assignment, 'a' and 'b' will be 
filled in order by the multiple return values of foo()

int foo() {
     return 1;
}

(int a, b) = foo(); // Also valid and only sets 'a'


int, int foo() {
     return 1, 2;
}

int a = foo(); // Also valid and 'a' takes the first tuple value

(int a, auto b) = foo(); // Evaluated left to right so 'a' will 
take the first argument of foo and b will auto to a type or tuple 
of what remains. It will error if there is nothing remaining for 
b. This would still allow the clean expression of stand-alone 
tuples and function arguments and return values.

int, string a = 1, "hello";
int, string foo(double, double a) {
    return cast(int) (d[0] * d[1]), "hello";
}

Doesn't the needs of bracketing to determine order operation, 
(stuff) more or less imply that implicit conversion between one 
member tuples and the type of that tuple member is a requirement? 
Or would the (stuff, ) syntax be better?
Sep 25 2012
prev sibling next sibling parent "David Piepgrass" <qwertie256 gmail.com> writes:
 The built-in tuple is also quite useful when defining templates.

 In essence, we have two kinds of tuples: the built-in language 
 tuple is the "unpacked" tuple while Phobos hosts the "packed" 
 one. They each have their own use case and they can coexist 
 peacefully. But the language itself needs to standardize on one 
 or the other.

+1, and it should standardize on "packed" (non-expanded) tuples because "unpacked" ones have very unusual behavior, and because it's impractical to eliminate "packed" tuples but practical to eliminate "unpacked" ones. "unpacked" tuples should only exist as an intermediate result (the result of .expand).
 If the language made T… a packed tuple instead, then we could 
 use the packed tuple everywhere and unpack it where necessary, 
 and something like this could be used to make a packed tuple:

 	T getThings(T...)(T.expand t)
 	{
 		return T(t);
 	}

 	T t1;
 	T t2 = getThings!(T)(t1.expand);

"T.expand" naturally has the connotation "unpacked" to me, whereas what you really want to do is indicate that "t" is packed, right? Clearly, the syntax for a varargs template like this would have to change to indicate that T is non-expanded; unfortunately, I don't have a really compelling syntax to suggest. P.S. If non-expanded tuples were the default, they should probably have a quicker syntax than "t.expand" to expand them. I suggest overloading unary * as in "*t"; this is known as the "explode" operator in boo.
Sep 25 2012
prev sibling next sibling parent "ixid" <nuaccount gmail.com> writes:
"T.expand" naturally has the connotation "unpacked" to me,

Isn't T.unpack clearer? Does that clash with a different usage for the term?
Sep 25 2012
prev sibling next sibling parent "ixid" <nuaccount gmail.com> writes:
I meant to reply to your post rather than Jacob's.
Sep 25 2012
prev sibling next sibling parent "ixid" <nuaccount gmail.com> writes:
On Tuesday, 25 September 2012 at 10:04:46 UTC, deadalnix wrote:
 Le 25/09/2012 03:19, ixid a écrit :
 What would a special case where the first level of tuple (with 
 higher
 levels being tuples in tuples) didn't require parens break? 
 This would
 be a beautiful syntax:

 auto a = 1, 2; // A tuple of two ints

 int, string fun(double, double d) {
 return cast(int) (d[0] * d[1]), "hello";
 }

 auto a, b = 1, 2; // Two ints
 auto a = fun(1.0, 1.0); // Tuple of 1 and "hello".
 auto a, b = fun(1.0, 1.0); // An int and a string.

It can get pretty confusing with , separated declarations : int a, b = 3; or worse : int a, int b = foo(); --> (int a, int b) = foo(); or int a, (int b = foo()); and it gets worse with int a, auto b = foo(); But I do agree that this is really nice in many places.

Replying to the correct post this time, sorry for the repeated posting. You've shown it's clearly incompatible with the current language and would break lots of code. What would it break if assignment required explicit tuple brackets? (int a, b) = foo(); // A tuple assignment, 'a' and 'b' will be filled in order by the multiple return values of foo() int foo() { return 1; } (int a, b) = foo(); // Also valid and only sets 'a' int, int foo() { return 1, 2; } int a = foo(); // Also valid and 'a' takes the first tuple value (int a, auto b) = foo(); // Evaluated left to right so 'a' will take the first argument of foo and b will auto to a type or tuple of what remains. It will error if there is nothing remaining for b. This would still allow the clean expression of stand-alone tuples and function arguments and return values. int, string a = 1, "hello"; int, string foo(double, double a) { return cast(int) (d[0] * d[1]), "hello"; } Doesn't the needs of bracketing to determine order operation, (stuff) more or less imply that implicit conversion between one member tuples and the type of that tuple member is a requirement? Or would the (stuff, ) syntax be better?
Sep 25 2012
prev sibling next sibling parent "foobar" <foo bar.com> writes:
On Tuesday, 25 September 2012 at 21:02:49 UTC, Andrei 
Alexandrescu wrote:
 I agree. That's why I want to take the minimum amount of steps 
 to make library tuples work. That minimum amount may be 1, i.e. 
 just implement deconstruction.

 Andrei

Library tuples have broken semantics. Tuples supposed to have _structural_ typing which AFAIK can only be correctly implemented in language. import std.typecons.TypeTuple; struct MyTuple(T...)() {} auto libTup = tuple(123, "hello"); MyTuple myTup = libTup; // broken This is broken cause structs in D are nominally typed and even though both pack the same inner-types, they are not equal. The problem with the lib solution is the confusion and broken semantics, _not_ the "tuple()" syntax. Sure, it's long and annoying to type for a [should be] common construct, but tuple *is* clear and readable, as you pointed out yourself. So syntax wise, I'm fine with both tuple(...) and a shorter syntax with some sort of parens-like character. But please, let's get at least the semantics absolutely right. I don't have a good suggestion how to fix this with no or minimal code breakage, but I don't thing that adding broken features the the mix helps any.
Sep 26 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 26 Sep 2012 15:54:44 -0400, foobar <foo bar.com> wrote:

 On Tuesday, 25 September 2012 at 21:02:49 UTC, Andrei Alexandrescu wrote:
 I agree. That's why I want to take the minimum amount of steps to make  
 library tuples work. That minimum amount may be 1, i.e. just implement  
 deconstruction.

 Andrei

Library tuples have broken semantics. Tuples supposed to have _structural_ typing which AFAIK can only be correctly implemented in language. import std.typecons.TypeTuple; struct MyTuple(T...)() {} auto libTup = tuple(123, "hello"); MyTuple myTup = libTup; // broken This is broken cause structs in D are nominally typed and even though both pack the same inner-types, they are not equal. The problem with the lib solution is the confusion and broken semantics, _not_ the "tuple()" syntax. Sure, it's long and annoying to type for a [should be] common construct, but tuple *is* clear and readable, as you pointed out yourself. So syntax wise, I'm fine with both tuple(...) and a shorter syntax with some sort of parens-like character. But please, let's get at least the semantics absolutely right. I don't have a good suggestion how to fix this with no or minimal code breakage, but I don't thing that adding broken features the the mix helps any.

I'm not exactly sure what this is supposed to be (your struct I don't think is implemented correctly), but are you asking to be able to assign a struct from a tuple? Shouldn't tupleof help here? -Steve
Sep 27 2012
prev sibling next sibling parent "ixid" <nuaccount gmail.com> writes:
 int, string a = 1, "hello";
 int, string foo(double, double a) {
 return cast(int) (d[0] * d[1]), "hello";
 }

 This is incompatible with current language specs (or will ends 
 up with highly bizantine rules do define what to do, in a topic 
 where it is already complex).

Which parts exactly are Byzantine and why? I'm not arguing, just interested to know as this is the part that seems most desirable to me.
Sep 27 2012
prev sibling parent "ixid" <nuaccount gmail.com> writes:
 int a, int b = 3, 3;

 Considering how the syntax is defined, this will not do what 
 you expect, and it is not fixable easily without breaking other 
 constructs.

I thought we'd already covered that part, that was what I agreed would break far too much code. That is not the heart of the suggestion though and is why I moved on to the same assignment syntax others were talking about using parens. (int a, int b) = 3, 3; or (int a, int b) = (3, 3); The parts, which perhaps your answer covers the issues with and I did not understand, that seem elegant that I was asking about were these: int, string a = 1, "hello"; int, string foo(double, double a) { return cast(int) (d[0] * d[1]), "hello"; } Tuple assignment to a tuple allowing the omission of the first level of brackets and multiple return type functions doing the same, at present these would be unambiguous errors as far as I'm aware. Especially with functions it seems a lot clearer to me and as we don't allow functions to do: void fun(int a, b, c) { //Stuff } To make a, b and c ints then we have the freedom to do multiple types separated by commas: void fun(int, string a) { } A syntax like this: (int, string) fun((double, double) a) { return (cast(int) (d[0] * d[1]), "hello"); } Is a lot messier and gets overloaded with parens.
Sep 28 2012
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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
Sep 23 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 12:11 AM, bearophile wrote:
 ...

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

I would think that it isn't an improvement at all. Disallowing some construct always will also disallow 'bad looking' code that uses it.
...

 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
 }

 ...

cases already introduce their own scopes in D, but switch cannot be extended well to serve such use cases. I agree with all the other points.
Sep 23 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 01:36 AM, bearophile wrote:
 Timon Gehr:

 cases already introduce their own scopes in D,

Thank you, I didn't remember this.
 but switch cannot be extended well to serve such use cases.

Please explain, as I am not able to see the problems.

Switch is syntax sugar for jump tables. An adequate pattern matching construct would not require explicit control flow statements, and it would be an expression of arbitrary type.
 I have discussed that topic a little here:
 http://d.puremagic.com/issues/show_bug.cgi?id=596

 Bye,
 bearophile

Sep 23 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-09-24 00:11, bearophile wrote:
 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.

+1 -- /Jacob Carlborg
Sep 24 2012
prev sibling next sibling parent "jerro" <a a.com> writes:
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:
Sep 23 2012
prev sibling next sibling parent "jerro" <a a.com> writes:
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.
Sep 23 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
Sep 23 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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);
Sep 23 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Sep 23 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 12:57 AM, Andrei Alexandrescu wrote:
 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:


Actually that is mostly unrelated to the comma operator. Apologies.
 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.

Imho, it certainly is the main issue.
 ...
 - 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?

Instances of TypeTuples, eg: template Seq(T...){ alias T Seq; } Seq!(int, double) foo(){ } => "Error: functions cannot return a tuple"
 - 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,

Well, (,) cannot help with that.
 for
 example a lambda that returns an empty tuple would be "()() {...}" and
 all that jazz.

Well, it would be ()=>() or delegate()()=>(), but is it even reasonable to use the second form?
 - 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.

I am discussing the interplay of the two features. Sequences are auto-expanded in all contexts where it makes sense (except if they happen to be the second argument to a comma expression, then they are not, but I assume that is a bug.) I expect the following to be equivalent: (Seq!(1,2,3),) and (1,2,3)
 - 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.

Not sure we are on the same page. I meant the calling convention at the ABI level.
 - As tuples are built-in, expansion can be shorter than '.expand'.
 foo(1, tup..., 3); ?

I find that sugar gratuitous.

You find built-in tuples gratuitous in general. :o) Anyway, it certainly is not necessary.
 - 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.

I assume named tuple fields are not a problem? Other than that, I raised the issue of how to match and destructure tuple types in template parameter lists. And came up with the following proposal, which I do not like. template Foo((U...,)){ alias (U,) Foo; } void main(){ (int, double) x; Foo!(typeof(x)) y; static assert(is(typeof(x)==typeof(y))); }
Sep 23 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 01:23, Timon Gehr a écrit :
 I assume named tuple fields are not a problem?

I'm not sure it is really usefull.
 Other than that, I raised the issue of how to match and destructure
 tuple types in template parameter lists. And came up with the following
 proposal, which I do not like.

 template Foo((U...,)){ alias (U,) Foo; }

 void main(){
 (int, double) x;
 Foo!(typeof(x)) y;
 static assert(is(typeof(x)==typeof(y)));
 }

The template syntax seems weird. Why do you want Foo((U...,)) when Foo(U) could do it ?
Sep 23 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 24/09/2012 01:34, bearophile a écrit :
 Andrei Alexandrescu:

 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.

As I have tried to explain in my precedent post, Kenji's patch covers about 1/4 of the most important use cases.

It would be great if we stopped to base design reflection on actual implementations we have somewhere.
Sep 23 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:

 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.

As I have tried to explain in my precedent post, Kenji's patch covers about 1/4 of the most important use cases. Bye, bearophile
Sep 23 2012
prev sibling next sibling parent "David Piepgrass" <qwertie256 gmail.com> writes:
 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");

Agreed, this is the key thing missing from D. There is also no consideration in the DIP of what I consider one of D's most confusing "features": "pre-expanded tuples" or in other words, type tuples. These beasts can be very confusing when first encountered, and they do not behave like any data type in any other language I know of: import std.typecons; // Contains Tuple!(...), which reminds me, // how do I know which module contains a given feature? // http://dlang.org/phobos/index.html doesn't mention it. void call() { humm(1, 2); } void humm(T...)(T x) // x, a pre-expanded tuple { //auto c = [x.expand]; // ERROR, expand undefined // (it's already expanded!) auto a = x; // a is also pre-expanded auto b = [ a, a ]; // int[], not Tuple!(int,int)[] //int d = derr(x); // ERROR, have to un-expand it writeln(a); // "12" writeln(b); // "[1, 2, 1, 2]" } int derr(Tuple!(int,int) a) { return a[0] + a[1]; } I know you guys are all used to this behavior but I'm telling you, pre-expanding is very weird. It would be nice if type tuples could somehow be unified with library tuples and behave like the latter.
Sep 24 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 24, 2012 16:00:06 David Piepgrass wrote:
 There is also no consideration in the DIP of what I consider one
 of D's most confusing "features": "pre-expanded tuples" or in
 other words, type tuples.

That's a completely separate issue. The DIP doesn't even introduce normal tuples into the language. It merely proposes that the comma operator be deprecated and uses the _possibility_ of introducing tuples into the language as an argument for that deprecation. - Jonathan M Davis
Sep 24 2012
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 23 September 2012 at 22:55:33 UTC, Timon Gehr wrote:
 I believe it is currently left-to-right for D, in all kinds of
 expressions, but DMD does not implement it yet.

Yeah, I thought it was already defined.
Sep 23 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Timon Gehr:

 cases already introduce their own scopes in D,

Thank you, I didn't remember this.
 but switch cannot be extended well to serve such use cases.

Please explain, as I am not able to see the problems. I have discussed that topic a little here: http://d.puremagic.com/issues/show_bug.cgi?id=596 Bye, bearophile
Sep 23 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Timon Gehr:

 Switch is syntax sugar for jump tables. An adequate pattern 
 matching
 construct would not require explicit control flow statements, 
 and it would be an expression of arbitrary type.

Introducing a good pattern matching syntax in D requires the introduction of a good amount of complexity. This idea was discussed few times in past. D switches also work on strings, wstrings, dstrings, so the idea of extending them to work on small array is not too much different. I think that switching on structs, and tuples with auto-assignment of variables for tuple fields, is enough to extend the usefulness of D switches significantly, while it introduces no new keywords, and not a lot of complexity for both the compiler and the programmer that has to learn D language. Bye, bearophile
Sep 23 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Sep 23, 2012 at 04:40:34PM -0400, 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

+1 to getting rid of the comma operator. (OK, just deprecation, but that's a good step.) I always felt it's one of those unnecessary holdovers from C. We don't need a comma operator. The only use cases I've ever seen of it is in specific contexts (such as for-loops) where suitable, relatively confined, syntax extensions would take care of everything without the invasiveness of having an entire operator. T -- I am a consultant. My job is to make your job redundant. -- Mr Tom
Sep 23 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Sun, 23 Sep 2012 21:30:08 -0700
"H. S. Teoh" <hsteoh quickfur.ath.cx> wrote:

 On Sun, Sep 23, 2012 at 04:40:34PM -0400, 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

+1 to getting rid of the comma operator. (OK, just deprecation, but that's a good step.) I always felt it's one of those unnecessary holdovers from C. We don't need a comma operator. The only use cases I've ever seen of it is in specific contexts (such as for-loops) where suitable, relatively confined, syntax extensions would take care of everything without the invasiveness of having an entire operator.

I saw it once in an earlier revision of RDMD from about a year ago. It was an awkward usage (IMO) and isn't there anymore. So basically, I agree that it's not particularly useful ;)
Sep 23 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Sun, 23 Sep 2012 18:48:22 -0400
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 
 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.
 

I think one of us is missing something, and I'm not entirely sure who. As I explained (perhaps poorly), the zero- and one-element tuples *would still be* tuples. They would just be implicitly convertible to non-tuple form *if* needed, and vice versa. Do you see a reason why that would *necessarily* not be the case?
 I think it's safe to just not even discuss it.

A nice way to put it :/ Part politician perhaps? ;)
Sep 23 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
Glad to hear that comma operator won't be depreciated.
Sep 23 2012
prev sibling next sibling parent reply Don Clugston <dac nospam.com> writes:
On 23/09/12 22:40, 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. 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.


 Thanks,

 Andrei

Regarding the comma operator: I'd love to deprecate it, but even if we don't, could we at least ensure that this kind of rubbish doesn't compile: void main() { int x; x > 0, x += 5; } At present, because comma expressions are expressions, not statements, the "x > 0" doesn't generate a "statement has no effect" error, despite the fact that it is meaningless and gets completely discarded.
Sep 24 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/24/12 4:17 AM, Don Clugston wrote:
 Regarding the comma operator: I'd love to deprecate it, but even if we
 don't, could we at least ensure that this kind of rubbish doesn't compile:

 void main()
 {
 int x;
 x > 0, x += 5;
 }

 At present, because comma expressions are expressions, not statements,
 the "x > 0" doesn't generate a "statement has no effect" error, despite
 the fact that it is meaningless and gets completely discarded.

Interesting. The comma operator is probably the only one in which an expression is evaluated only for the sake of its side effects. So eliminating the comma operator would just get rid of that case by design. Of course, there's always the option of adding more checks or rewriting the comma operator from "expr1, expr2, expr3" to "{ expr1; expr2; return expr3; }()". Andrei
Sep 24 2012
parent Don Clugston <dac nospam.com> writes:
On 24/09/12 17:19, Andrei Alexandrescu wrote:
 On 9/24/12 4:17 AM, Don Clugston wrote:
 Regarding the comma operator: I'd love to deprecate it, but even if we
 don't, could we at least ensure that this kind of rubbish doesn't
 compile:

 void main()
 {
 int x;
 x > 0, x += 5;
 }

 At present, because comma expressions are expressions, not statements,
 the "x > 0" doesn't generate a "statement has no effect" error, despite
 the fact that it is meaningless and gets completely discarded.

Interesting. The comma operator is probably the only one in which an expression is evaluated only for the sake of its side effects. So eliminating the comma operator would just get rid of that case by design.

Yes. Comma is a special case in a number of ways.
 Of course, there's always the option of adding more checks or rewriting
 the comma operator from "expr1, expr2, expr3" to "{ expr1; expr2; return
 expr3; }()".

We hit this one often in real-world code. On German keyboards , and ; are on the same key, so it's a fairly easy typo. I don't think it happens as often when using a US keyboard.
Sep 25 2012
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-09-23 22:40, 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

+1 -- /Jacob Carlborg
Sep 24 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 24 Sep 2012 10:56:40 +0200
Jacob Carlborg <doob me.com> wrote:

 On 2012-09-24 07:01, Nick Sabalausky wrote:
 
 I think one of us is missing something, and I'm not entirely sure
 who.

 As I explained (perhaps poorly), the zero- and one-element tuples
 *would still be* tuples. They would just be implicitly convertible
 to non-tuple form *if* needed, and vice versa. Do you see a reason
 why that would *necessarily* not be the case?

Would that mean you could start doing things like: int a = 3; int b = a[0]; That feels very weird.

No, because there's nothing typed (int) involved there. But you could do this: int a = 3; (int) b = a; a = b; Or this: void foo((int) a) { int b1 = a[0]; int b2 = a; } int c = 3; foo(c);
Sep 24 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 24 Sep 2012 10:47:38 +0200
"foobar" <foo bar.com> wrote:
 
 Nope.
 One of the ways in math to "build" the positive numbers based on 
 set theory is via singletons:
 n := |tuple of empty tuples|
 so "1" is defined as { {} } whereas "0" is simply {}. That does 
 not work with the above suggestion. Now, I realize this is an 
 arguably convoluted math example but it does show that the 
 treating { {} } as {} is limiting the expressive power of tuples.
 

And int's are limiting compared to mathematical integers. So what? So ok, maybe this is limiting from a theoretical standpoint. But practically speaking? I dunno. We're not making tuples to emulate set theory here, we're just looking for ad-hoc anonymous structs. Besides, I only said they were logically the same thing, not mechanically. I'm only suggesting that a one-element tuple be implicitly convertible to/from the type of its element. So there would likely still be the different types, it just makes sense that you should be able to use one as the other.
Sep 24 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 24 Sep 2012 06:20:55 -0400
Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> wrote:

 On Mon, 24 Sep 2012 10:47:38 +0200
 "foobar" <foo bar.com> wrote:
 
 Nope.
 One of the ways in math to "build" the positive numbers based on 
 set theory is via singletons:
 n := |tuple of empty tuples|
 so "1" is defined as { {} } whereas "0" is simply {}. That does 
 not work with the above suggestion. Now, I realize this is an 
 arguably convoluted math example but it does show that the 
 treating { {} } as {} is limiting the expressive power of tuples.
 

And int's are limiting compared to mathematical integers. So what? So ok, maybe this is limiting from a theoretical standpoint. But practically speaking? I dunno. We're not making tuples to emulate set theory here, we're just looking for ad-hoc anonymous structs. Besides, I only said they were logically the same thing, not mechanically. I'm only suggesting that a one-element tuple be implicitly convertible to/from the type of its element. So there would likely still be the different types, it just makes sense that you should be able to use one as the other.

I guess what I mean is: If you really need a strict separation between a "tuple of one" and a non-tuple, then maybe it's an indication that you're just using the wrong tool? That said, I'm not necessarily opposed to the strict separation if we had a good candidate for built-in tuple literal syntax. But *if* the best we have is parens (and maybe there *is* something better?) then maybe this would be an acceptable way to achieve it? Ie: // (3) is polysemous: Either int or (int) int a = (3); // Normal value (int) b = (3); // One-element tuple auto c = (3); // Default to normal "int"? void foo(int z) {} void foo((int) z) {} void takeInt(int z) {} void takeTuple((int) z) {} foo(a); // Calls first overload foo(b); // Calls second overload takeInt(a); // ok takeInt(b); // ok takeTuple(a); // ok takeTuple(b); // ok
Sep 24 2012
prev sibling next sibling parent reply Caligo <iteronvexor gmail.com> writes:
If tuples are ever introduced, I hope parentheses will not be used.

I would prefer something like this:

tuple<2,1,8>
Sep 24 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/24/2012 09:50 PM, Caligo wrote:
 On Mon, Sep 24, 2012 at 11:37 AM, bearophile <bearophileHUGS lycos.com> wrote:
 That both breaks code, doesn't improve the syntax, but makes it worse.

 Bye,
 bearophile

foo(<11,2,8>, a, b) vs foo((11,2,8), a, b)

I don't spot a significant difference.
 Parentheses are everywhere in D.  Sometimes it looks like Lisp.

Lisp is beautiful.
Sep 24 2012
prev sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
On 09/24/2012 11:55 AM, Caligo wrote:
 If tuples are ever introduced, I hope parentheses will not be used.

 I would prefer something like this:

 tuple<2,1,8>

Not using parentheses: a possibly valid idea! Using angle brackets: never going to happen. People HATE angle brackets. There is extremely good justification for this hatred, because C++ already stepped on that landmine and suffered dearly for it. If you were to use angle brackets, then I would try to do this: tuple<a>b,c> d Which way should this be parsed? It might be a value tuple of type <bool,bool> or it might be a declaration: d is of type <bool, typeof(c)> It is very easy to create cases with angle brackets that are syntactically ambiguous and make it impossible to parse code as a context-free-grammar. It is harder, but probably possible, to create cases where such a syntax is also completely ambiguous semantically too.
Oct 06 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Caligo:

 If tuples are ever introduced, I hope parentheses will not be 
 used.

 I would prefer something like this:

 tuple<2,1,8>

That both breaks code, doesn't improve the syntax, but makes it worse. Bye, bearophile
Sep 24 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Sunday, September 23, 2012 16:40:34 Andrei Alexandrescu wrote:
 http://www.prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP19

My #1 concern here is that for loops do _not_ ever change how they function with regards to commas (which the DIP seems to do, but it also seems to imply that we might want to get rid of that later - which I do _not_ agree with). The comma operator is occasionally useful beyond for loops, but it's usually considered bad practice to do so, so if we want to get rid of it aside from for loops, then I have no problem with that. If anything, I'd argue that bringing tuples into the mix is muddying matters, since I think that there's a solid argument for deprecating the comma operator based solely on the problems that it causes even if we never add any other syntax which uses commas in a way that the comma operator prevents. As to whether we add tuples or not, I don't know. Being able to do something like. int i; string s; (i, s) = foo(); or (auto i, string is) = foo(); would be useful, but I can live without it. std.typecons.tuple takes care of most of what you need from tuples IMHO. So, if we can find a way to cleanly add tuples to the language, I'm fine with that, but I'm also fine with leaving tuples as they are. - Jonathan M Davis
Sep 24 2012
prev sibling next sibling parent Caligo <iteronvexor gmail.com> writes:
foo(<11,2,8>, a, b)
vs
foo((11,2,8), a, b)

Parentheses are everywhere in D.  Sometimes it looks like Lisp.

On Mon, Sep 24, 2012 at 11:37 AM, bearophile <bearophileHUGS lycos.com> wrote:
 That both breaks code, doesn't improve the syntax, but makes it worse.

 Bye,
 bearophile

Sep 24 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 24 Sep 2012 21:16:06 +0200
Jacob Carlborg <doob me.com> wrote:

 On 2012-09-24 15:05, deadalnix wrote:
 
 I understand your example, but in it, no (int) are involved. So no
 conversion have to be done (and you get an error).

What has that to do with anything. Example: auto a = 3; There's no mention of "int" in that example, yet "a" is still an int.

Of course there is, it's the default type for the literal you have there.
 You see in example above that conversion is done when int is given
 where (int) is expected or vice versa, not whenever the compiler
 feels to.

int b = 4; b[0] Why isn't that an example of where a (int) is expected?

Because 'b' is neither being assigned to an (int) nor passed into a template/func parameter that's expecting an (int).
Sep 24 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 24 Sep 2012 16:50:47 +0200
"foobar" <foo bar.com> wrote:

 On Monday, 24 September 2012 at 10:05:18 UTC, Nick Sabalausky 
 wrote:
 On Mon, 24 Sep 2012 10:56:40 +0200
 Jacob Carlborg <doob me.com> wrote:

 On 2012-09-24 07:01, Nick Sabalausky wrote:
 
 I think one of us is missing something, and I'm not entirely 
 sure
 who.

 As I explained (perhaps poorly), the zero- and one-element 
 tuples
 *would still be* tuples. They would just be implicitly 
 convertible
 to non-tuple form *if* needed, and vice versa. Do you see a 
 reason
 why that would *necessarily* not be the case?

Would that mean you could start doing things like: int a = 3; int b = a[0]; That feels very weird.

No, because there's nothing typed (int) involved there. But you could do this: int a = 3; (int) b = a; a = b; Or this: void foo((int) a) { int b1 = a[0]; int b2 = a; } int c = 3; foo(c);

What's the point than? here's equivalent code without this "feature": int a = 3; (int) b = (a); // explicitly make 1-tuple

My understanding is that *can't* be made to work in the general case (without those ugly trailing commas) because, in general, how's the compiler supposed to know if (a) is a parenthesis expression or a tuple literal? That's exactly what my suggestion was attempting to solve: The '(a)' would be a paren expression (with type 'int') just as right now, but then in order to make it still assignable to '(int)', just as you've done, we say "Ok, you can assign an 'int' to an '(int)' and it implicitly converts." All the stuff I said earlier about one-element tuples being conceptually the same as non-tuples was just my way of explaining that it's not too much of an unintuitive inconsistency if we allow implicit packing/unpacking of one-element tuples, but not two-or-more-element tuples.
Sep 24 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 24 Sep 2012 15:27:19 +0200
Philippe Sigaud <philippe.sigaud gmail.com> wrote:

 On Mon, Sep 24, 2012 at 12:46 PM, Nick Sabalausky
 <SeeWebsiteToContactMe semitwist.com> wrote:
 
 That said, I'm not necessarily opposed to the strict separation if
 we had a good candidate for built-in tuple literal syntax. But *if*
 the best we have is parens (and maybe there *is* something better?)
 then maybe this would be an acceptable way to achieve it?

If the problems in DIP 19 are deemed mostly syntactic (1- and 0- element tuples), then maybe *for once* a simple syntax change could solve them? I know syntax proposals are a dime a dozen in this newsgroup, but why not here, to avoid the ((1)) problem?

I'm all for that. In fact, I was was just about to post the same suggestion. My bikeshed is colored one of these: (:1,2) (|1,2) Minimal syntax (one extra character), no ambiguities with anything else AFAIK. Looks kinda funny, but so did !() at first and we all got used to that.
 For example choosing { 1, 2} to represent a tuple?

I like it, but what about zero-element tuples vs empty code blocks? (Or is it ok because code blocks can't be used inside, or as, expressions?) Also, it may be too easy to accidentally get mixups between one-element tuples and certain one-statement blocks: { foo(); } // Block vs { foo() } // Either a tuple or a forgotten semicolon Not sure if that's a big enough deal, though.
 Ie:

 // (3) is polysemous: Either int or (int)
 int   a = (3);  // Normal value
 (int) b = (3);  // One-element tuple
 auto  c = (3);  // Default to normal "int"?

For the third case, I'd say it defaults to a tuple. But then again, using another syntax solves this problem.

My reasoning for defaulting to non-tuple was minimizing code breakage and simplifying the handling of general expresssions that happen to involve parens (ie, it's always a mere expression until it gets assigned/passed-in to a tuple). But I agree, just using a syntax that's unambiguous from the start is better.
Sep 24 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 24 Sep 2012 10:53:14 -0400
"Steven Schveighoffer" <schveiguy yahoo.com> wrote:
 
 (int[]) x;
 
 int a = x.length;
 
 is a == 0 or 1?
 

It'd be 1, but I agree that's a pretty solid counter-argument.
Sep 24 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Mon, 24 Sep 2012 12:51:18 -0400
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 On 9/24/12 11:47 AM, Steven Schveighoffer wrote:
 I just wanted to point out that it seems the largest trouble,
 implementation-wise, for DIP19 is the choice of parentheses to
 denote a tuple. If we do want to add built-in tuples, maybe we
 should be looking at a different delimiter.

Indeed. The question is what mileage we get out of it.

Since the issues with current tuples tend to discourage their use (at least for me anyway), it's hard to say without having them. Maybe it would help to look at languages that do have good tuples and see what kind of mileage they get out of them?
Sep 24 2012
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Mon, 24 Sep 2012 21:50:34 +0200, Caligo <iteronvexor gmail.com> wrote:

 foo(<11,2,8>, a, b)
 vs
 foo((11,2,8), a, b)

 Parentheses are everywhere in D.  Sometimes it looks like Lisp.

And <> is ambiguous, ugly, an affront before Walter, and an abomination born in the fiery depths of hell. Can we please just leave it behind? -- Simen
Sep 24 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/23/12, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 http://www.prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP19

Oh, I completely forgot to mention this little bug I've had due to the comma operator a few weeks ago: import std.stdio; int getInt(string op) { if (op, "a") return 1; else if (op == "b") return 2; else return 3; } void main() { string op1 = "a"; string op2 = "b"; string op3 = "c"; writeln(getInt(op1)); writeln(getInt(op2)); writeln(getInt(op3)); } It was a result of a refactoring and the bug went unnoticed until I started getting weird results back. The if/else was much bigger and the comma was somewhere in the middle. So yeah, nuke it from orbit!
Sep 25 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, September 25, 2012 13:45:50 Michel Fortin wrote:
 On 2012-09-25 16:38:31 +0000, "Jonathan M Davis" <jmdavisProg gmx.com> said:
 On Tuesday, September 25, 2012 12:28:15 Michel Fortin wrote:
 Although to make things less confusing, I think the built-in language
 tuple should give up its name. It could become a "sequence". Renaming
 the built-in one would certainly be less trouble, as code doesn't refer
 to it by its name, and you can pick a name that fits better with its
 auto-expanding behaviour.

Not referred to by its name? It's refereed to as TypeTuple all over the place. It's arguably a bad name, but it would break a _lot_ of code to change it now.

language tuple, one that does not necessarily match the definition of a TypeTuple. The language spec defines a Tuples, TypeTuples, and ExpressionTuples with these words (ironically, using the word "sequence" twice): """ If the last template parameter in the TemplateParameterList is declared as a TemplateTupleParameter, it is a match with any trailing template arguments. The sequence of arguments form a Tuple. A Tuple is not a type, an expression, or a symbol. It is a sequence of any mix of types, expressions or symbols. A Tuple whose elements consist entirely of types is called a TypeTuple. A Tuple whose elements consist entirely of expressions is called an ExpressionTuple. """ Source: http://dlang.org/template.html

I wasn't aware of that being in the language definition, but it doesn't change the fact that they're used and referred to in code as TypeTuple, and renaming that would break a lot of code. And it _is_ used for the built-in tuple type, regardless of whether the spec considers the terms type tuple and expression tuple to refer to distinct entities. Rename the stuff in the spec to whatever you like, but the library uses the term TypeTuple, so it _is_ used in code. - Jonathan M Davis
Sep 25 2012
prev sibling next sibling parent kenji hara <k.hara.pg gmail.com> writes:
2012/9/26 Jonathan M Davis <jmdavisProg gmx.com>:
 On Tuesday, September 25, 2012 13:45:50 Michel Fortin wrote:
 On 2012-09-25 16:38:31 +0000, "Jonathan M Davis" <jmdavisProg gmx.com> said:
 On Tuesday, September 25, 2012 12:28:15 Michel Fortin wrote:
 Although to make things less confusing, I think the built-in language
 tuple should give up its name. It could become a "sequence". Renaming
 the built-in one would certainly be less trouble, as code doesn't refer
 to it by its name, and you can pick a name that fits better with its
 auto-expanding behaviour.

Not referred to by its name? It's refereed to as TypeTuple all over the place. It's arguably a bad name, but it would break a _lot_ of code to change it now.

language tuple, one that does not necessarily match the definition of a TypeTuple. The language spec defines a Tuples, TypeTuples, and ExpressionTuples with these words (ironically, using the word "sequence" twice): """ If the last template parameter in the TemplateParameterList is declared as a TemplateTupleParameter, it is a match with any trailing template arguments. The sequence of arguments form a Tuple. A Tuple is not a type, an expression, or a symbol. It is a sequence of any mix of types, expressions or symbols. A Tuple whose elements consist entirely of types is called a TypeTuple. A Tuple whose elements consist entirely of expressions is called an ExpressionTuple. """ Source: http://dlang.org/template.html

I wasn't aware of that being in the language definition, but it doesn't change the fact that they're used and referred to in code as TypeTuple, and renaming that would break a lot of code. And it _is_ used for the built-in tuple type, regardless of whether the spec considers the terms type tuple and expression tuple to refer to distinct entities. Rename the stuff in the spec to whatever you like, but the library uses the term TypeTuple, so it _is_ used in code. - Jonathan M Davis

I like current design - open (built-in, automatically flattened, and *unpacked*) tuple, and closed (library, be structured, and *packed*) tuple. But, the two are often confused, by the word "tuple". It has introduced badly confusion in many discussions. To make matters worse, it had often invoked incorrect suggestion that merging the two into one. My suggestion is very simple. 1. Change all words "built-in tuple" in the documentation to "built-in sequence". Then, in the D language world, we can have clarify name for the built-in one. 2. Introduce new templates, Seq, TypeSeq, and ExpSeq. template Seq(T...) { alias T Seq; } // identical with std.typetuple.TypeTuple template TypeSeq(T...) if (allSatisfy!(isType, T)) { alias T TypeSeq; } template ExpSeq(T...) if (allSatisfy!(isExpression, T)) { alias T ExpSeq; } If you really want to a sequence with heterogeneous elements, use Seq template. Otherwise use TypeSeq or ExpSeq based on your purpose. Kenji Hara
Sep 25 2012
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, September 26, 2012 04:29:13 kenji hara wrote:
 But, the two are often confused, by the word "tuple". It has
 introduced badly confusion in many discussions.
 To make matters worse, it had often invoked incorrect suggestion that
 merging the two into one.
 
 My suggestion is very simple.
 1. Change all words "built-in tuple" in the documentation to "built-in
 sequence". Then, in the D language world, we can have clarify name for
 the built-in one.
 2. Introduce new templates, Seq, TypeSeq, and ExpSeq.
 
 template Seq(T...) { alias T Seq; } // identical with
 std.typetuple.TypeTuple
 template TypeSeq(T...) if (allSatisfy!(isType, T)) { alias T TypeSeq; }
 template ExpSeq(T...) if (allSatisfy!(isExpression, T)) { alias T
 ExpSeq; }
 
 If you really want to a sequence with heterogeneous elements, use
 Seq template. Otherwise use TypeSeq or ExpSeq based on your purpose.

In principle, renaming TypeTuple makes sense given it's bad name (though I really don't seem much point in separating expression tuples and type tuples), but it would break a _lot_ of code. And both Walter and Andrei are increasingly against making any breaking changes. - Jonathan M Davis
Sep 25 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/25/12 4:14 PM, Jonathan M Davis wrote:
 On Wednesday, September 26, 2012 04:29:13 kenji hara wrote:
 But, the two are often confused, by the word "tuple". It has
 introduced badly confusion in many discussions.
 To make matters worse, it had often invoked incorrect suggestion that
 merging the two into one.

 My suggestion is very simple.
 1. Change all words "built-in tuple" in the documentation to "built-in
 sequence". Then, in the D language world, we can have clarify name for
 the built-in one.
 2. Introduce new templates, Seq, TypeSeq, and ExpSeq.

 template Seq(T...) { alias T Seq; } // identical with
 std.typetuple.TypeTuple
 template TypeSeq(T...) if (allSatisfy!(isType, T)) { alias T TypeSeq; }
 template ExpSeq(T...) if (allSatisfy!(isExpression, T)) { alias T
 ExpSeq; }

 If you really want to a sequence with heterogeneous elements, use
 Seq template. Otherwise use TypeSeq or ExpSeq based on your purpose.

In principle, renaming TypeTuple makes sense given it's bad name (though I really don't seem much point in separating expression tuples and type tuples), but it would break a _lot_ of code. And both Walter and Andrei are increasingly against making any breaking changes. - Jonathan M Davis

TypeTuple does a lot of harm. I'd be glad to rename it GenericTuple and leave TypeTuple as a slowly rotting alias. Andrei
Sep 25 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/25/2012 11:01 PM, Andrei Alexandrescu wrote:
 On 9/25/12 4:14 PM, Jonathan M Davis wrote:
 On Wednesday, September 26, 2012 04:29:13 kenji hara wrote:
 But, the two are often confused, by the word "tuple". It has
 introduced badly confusion in many discussions.
 To make matters worse, it had often invoked incorrect suggestion that
 merging the two into one.

 My suggestion is very simple.
 1. Change all words "built-in tuple" in the documentation to "built-in
 sequence". Then, in the D language world, we can have clarify name for
 the built-in one.
 2. Introduce new templates, Seq, TypeSeq, and ExpSeq.

 template Seq(T...) { alias T Seq; } // identical with
 std.typetuple.TypeTuple
 template TypeSeq(T...) if (allSatisfy!(isType, T)) { alias T TypeSeq; }
 template ExpSeq(T...) if (allSatisfy!(isExpression, T)) { alias T
 ExpSeq; }

 If you really want to a sequence with heterogeneous elements, use
 Seq template. Otherwise use TypeSeq or ExpSeq based on your purpose.

In principle, renaming TypeTuple makes sense given it's bad name (though I really don't seem much point in separating expression tuples and type tuples), but it would break a _lot_ of code. And both Walter and Andrei are increasingly against making any breaking changes. - Jonathan M Davis

TypeTuple does a lot of harm. I'd be glad to rename it GenericTuple and leave TypeTuple as a slowly rotting alias. Andrei

I'd never use GenericTuple, because already after four usages it would have been cheaper to just define Seq inline. GenericTupleGenericTupleGenericTupleGenericTuple template Seq(T...){ alias T Seq; }SeqSeqSeqSeq What is the rationale for calling this construct a tuple anyway? Programming language tuples usually impose more structure on the data than just ordering.
Sep 25 2012
prev sibling next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Tue, 25 Sep 2012 12:07:33 +0200
deadalnix <deadalnix gmail.com> wrote:

 Le 25/09/2012 09:11, Jacob Carlborg a =C3=A9crit :
 On 2012-09-25 00:28, bearophile wrote:

 (||)
 (|1|)
 (|1, 2|)
 (|1, 2, 3|)

What about: || |1| |1, 2|

Yeah and why not =C3=BE1, 2=C3=BE or =C5=801, 2=C5=80 ? =20 maybe =E2=86=931, 2=E2=86=93 is better ? =20 or =C2=AB 1, 2 =C2=BB (this one at least is readable).

| is easily typed. =C3=BE, =C5=80, =E2=86=93 and =C2=AB I had to copy-paste.
Sep 25 2012
prev sibling next sibling parent "Michael" <pr m1xa.com> writes:
 Also, I want to add that type declarations should be changed 
 from statements to expressions so that we could do:
 auto tup = (3, "hello");
 (int num, string s) = tup; // num == 3, s == "hello"

+1. or int num; string s; auto tup = (3, "hello"); (num, s) = tup; or like x++ containers http://msdn.microsoft.com/en-us/library/aa874816.aspx auto tup = [3, "hello"]; [num, s] = tup; In general it's very useful.
Sep 26 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, September 27, 2012 00:05:41 bearophile wrote:
 Jonathan M Davis:
 So, this really makes no sense to me at all.

I agree that foobar examples aren't so good. But it's true that Tuple() gives some troubles regarding missed structural typing: import std.stdio, std.typecons; alias Tuple!(float, float) T1; alias Tuple!(float,"x", float,"y") T2; void foo1(T1 t) {} void foo2(T2 t) {} void main() { auto p1 = T1(1, 2); auto p2 = T2(1, 2); p1 = p2; // no error p2 = p1; // no error T1[] a1; T2[] a2; a1 = a2; // error a2 = a1; // error foo1(p2); // error foo2(p1); // error } Generally I think "p1 = p2;" is OK, while "p2 = p1;" is not so good, because T2 is more specialized. There are several other more or less similar things related to structural typing of tuples that a well implemented built-in tuple type will need to address. In Bugzilla there are some open bug reports on similar matters. Fixing them with the library defined tuples is hard. Of course a possible solution is to improve the D language to allow the programmer to specify very good struct-based tuples in library code, I think but doing this is more complex than implementing good built-in tuples.

It sounds to me like the reason that structural typing is needed is because Tuple allows you to name its fields, which I've always thought was a bad idea, and which a built-in tuple definitely wouldn't do. If you couldn't name its fields, then any Tuple containing the same sequence of types would be the same type. So, the problem is caused by a feature that built-in tuples wouldn't even have. - Jonathan M Davis
Sep 26 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 It sounds to me like the reason that structural typing is 
 needed is because
 Tuple allows you to name its fields, which I've always thought 
 was a bad idea,

I have appreciated named fields of D tuples since the beginning, I have found them quite handy. With them sometimes you don't need to unpack a tuple, you can just access its fields with a nice name, avoiding to move around more than one variable. In Python there are build-in tuples, and there is a library-defined tuple type that supports names for its fields, it even contains its name, so when you print one of them, it's able to offer a nice textual representation:
 from collections import namedtuple
 Point = namedtuple('Point', 'x y')
 Point(5, 10)



Bye, bearophile
Sep 26 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
 I have appreciated named fields of D tuples since the 
 beginning, I have found them quite handy. With them sometimes 
 you don't need to unpack a tuple, you can just access its 
 fields with a nice name, avoiding to move around more than one 
 variable.

In Haskell to solve this problem there is the syntax for tuples. As an example usage, this Haskell function vNorm normalizes a Vec (a 2D vector). Its input is a Vec, that is unpacked in its x and y fields, but vNorm al gives a name to the whole input Vec, naming it 'v'. So you are able to call the other function vLen with no need to pack again x and y in a Vec: vLen :: Vec -> Double vLen x = sqrt $ vDot x x vNorm :: Vec -> Vec vNorm v (Vec x y) = Vec (x / l) (y / l) where l = vLen v (In my first post in this thread I have not listed such extra features for tuples because I think they are less important than a good unpacking syntax in those four cases.) Bye, bearophile
Sep 26 2012
prev sibling next sibling parent "foobar" <foo bar.com> writes:
On Wednesday, 26 September 2012 at 23:02:45 UTC, Piotr Szturmaj 
wrote:
 Jonathan M Davis wrote:
 It sounds to me like the reason that structural typing is 
 needed is because
 Tuple allows you to name its fields, which I've always thought 
 was a bad idea,
 and which a built-in tuple definitely wouldn't do. If you 
 couldn't name its
 fields, then any Tuple containing the same sequence of types 
 would be the same
 type. So, the problem is caused by a feature that built-in 
 tuples wouldn't
 even have.

Exactly my PoV. I think that "tuples with named fields" should be anonymous structs and pure tuples shouldn't have named fields.

I agree. Tuples do *not* have field names. (I'm also not sure they should support slicing either). structural compound types with field names are called "records" in FP and they are a completely separate concept from tuples. We really should not conflate the two and I agree that nameless structs are the perfect vehicle to support record types. One of D's strongest design decisions was to separate structs from classes which is a huge win. Why do we want to go back on that with regards to this very similar use case?
Sep 27 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, September 27, 2012 11:37:10 foobar wrote:
 I do _not_ want to consider two different _structs_ (nominal
 types) as the same type. I would like to get correct tuple
 semantics which means _structural_ typing (I thought I emphasized
 that enough in the OP).
 A tuple is defined by its contained types, *not* its name.

What on earth does structural typing get you here? A tuple is a collection of values of varying types. If you have Tuple!(X, Y, Z), it defines a tuple containing the types X, Y, and Z. _All_ tuples with those types will be Tuple! (X, Y, Z). The _only_ reason that this isn't quite the case is the nonsense with being able to give names to the fields in a tuple (and adding an alias this to Tuple should be able to fix that). Being able to create your own tuple type which you can compare with Tuple simply because it happens to hold the same types is completely pointless as far as I can tell (but you can still do it if you really want to). If you want a tuple, then just use std.typecons.Tuple. Creating another tuple type buys you nothing. - Jonathan M Davis
Sep 27 2012
prev sibling next sibling parent "foobar" <foo bar.com> writes:
On Thursday, 27 September 2012 at 10:58:12 UTC, Jonathan M Davis 
wrote:
 On Thursday, September 27, 2012 11:37:10 foobar wrote:
 I do _not_ want to consider two different _structs_ (nominal
 types) as the same type. I would like to get correct tuple
 semantics which means _structural_ typing (I thought I 
 emphasized
 that enough in the OP).
 A tuple is defined by its contained types, *not* its name.

What on earth does structural typing get you here? A tuple is a collection of values of varying types. If you have Tuple!(X, Y, Z), it defines a tuple containing the types X, Y, and Z. _All_ tuples with those types will be Tuple! (X, Y, Z). The _only_ reason that this isn't quite the case is the nonsense with being able to give names to the fields in a tuple (and adding an alias this to Tuple should be able to fix that). Being able to create your own tuple type which you can compare with Tuple simply because it happens to hold the same types is completely pointless as far as I can tell (but you can still do it if you really want to). If you want a tuple, then just use std.typecons.Tuple. Creating another tuple type buys you nothing. - Jonathan M Davis

std.typecons.Tuple *is* a struct. I agree with the above definition of tuples, but I want the language to ensure that which at the moment it can't.
Sep 27 2012
prev sibling next sibling parent "David Nadlinger" <see klickverbot.at> writes:
On Wednesday, 26 September 2012 at 12:20:56 UTC, Dmitry Olshansky 
wrote:
 On 25-Sep-12 23:29, kenji hara wrote:
 My suggestion is very simple.
 1. Change all words "built-in tuple" in the documentation to 
 "built-in
 sequence". Then, in the D language world, we can have clarify 
 name for
 the built-in one.
 2. Introduce new templates, Seq, TypeSeq, and ExpSeq.

     template Seq(T...) { alias T Seq; }    // identical with
 std.typetuple.TypeTuple
     template TypeSeq(T...) if (allSatisfy!(isType, T)) { alias 
 T TypeSeq; }
     template ExpSeq(T...) if (allSatisfy!(isExpression, T)) { 
 alias T ExpSeq; }

   If you really want to a sequence with heterogeneous 
 elements, use
 Seq template. Otherwise use TypeSeq or ExpSeq based on your 
 purpose.


vote++. (This is also what would be in my never finished std.meta proposal – interesting how we seem to converge towards the same solutions in the metaprogramming space…) David
Sep 27 2012
prev sibling next sibling parent "Michael" <pr m1xa.com> writes:
On Sunday, 23 September 2012 at 20:39:38 UTC, 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. 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.


 Thanks,

 Andrei

With removed comma operator from D next code will work? module maybe; import std.stdio; import std.algorithm; R With(I, R)(I o, R function (I) fun) { return o is null ? null : fun(o); } R Return(I, R)(I o, R function (I) fun, R failureResult) { return o is null ? failureResult : fun(o); } I If(I)(I o, bool function (I) fun) { return o is null ? null : fun(o) ? o : null; } I Unless(I)(I o, bool function (I) fun) { return o is null ? null : fun(o) ? null : o; } I Do(I)(I o, void function (I) fun) { return o is null ? null : fun(o), o; } void doit(string value) { writeln("Loading... \n"); } void main() { string name = "D"; name = name.With((string x) => "Hello, " ~ x ~ "!"). If((string x) => canFind(x, "D")). Do((string x) => x.doit). Return((string x) => x ~ " I love You!", "Oh, my!"); writeln(name); } especially template function I Do(I)(I o, void function (I) fun) { return o is null ? null : fun(o), o; }
Oct 06 2012
prev sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
On 09/23/2012 04: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. 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.


 Thanks,

 Andrei

Might some of the ambiguity be reduced by defining special tokens for dereferencing tuple elements? (int[]) a = ([1,2,3]); assert(a[0] == 1); assert(a[[0]] == [1,2,3]); I suspect that this would allow us to indulge in the attractive notion of single-element tuples. Would the addition of such a token be considered heresy?
Oct 06 2012