Thread overview
Re: Tuple literal syntax + Tuple assignment
Oct 07, 2010
bearophile
Oct 07, 2010
Simen kjaeraas
Oct 07, 2010
kenji hara
Oct 07, 2010
kenji hara
Oct 07, 2010
Michel Fortin
Oct 07, 2010
retard
Oct 29, 2010
Bruno Medeiros
October 07, 2010
In the two threads (that are a single thread) most of the things I've seen are bad/wrong.

I have discussed about Tuples several times in the D newsgroup and in Bugzilla. Please don't ignore all my work.

Before designing tuple syntax you must decide what the purpose of D tuples is. Then you have to solve the design problems, and avoid all (or most) corner cases. In this discussion it's useful to have a certain experience of languages that use tuples often, as Python and others.

Tuples have some purposes:
- Python, Go and other languages show that it's handy to allow functions to return multiple values, this means a tuple.
- A handy tuple unpacking is useful at the calling point of a function that returns multiple return values.
- Tuples are also useful as quick-and-dirty structs, to sort items in a different order, etc.

It's useful to use [] to access tuple items, to slice tuples, concat them. It's useful for tuples to have a good textual representation, to be comparable lexicographically and to be hashable.

Another design decision is if tuples have a nominative or structural type, this problem comes out in this bug report: http://d.puremagic.com/issues/show_bug.cgi?id=4128

In my opinion it's good for a built-in D tuple to be a structural type. This also means you are allowed to perform an == among two tuples of different length (the result is known statically to be always false). I assume that D tuples know their length at compile-time.


Another significant problem is about naming things, currently the situation is a mess:
http://www.digitalmars.com/d/archives/digitalmars/D/Tuple_TypeTuple_tupleof_etc_113005.html
http://d.puremagic.com/issues/show_bug.cgi?id=4113
In the end I have suggested to name "record" the typecons tuples, and "tuple" the typetuples.


I have several bug reports and enhancement requests about tuples, please take them into account:

http://d.puremagic.com/issues/show_bug.cgi?id=4577 http://d.puremagic.com/issues/show_bug.cgi?id=4582 http://d.puremagic.com/issues/show_bug.cgi?id=4591 http://d.puremagic.com/issues/show_bug.cgi?id=4666 http://d.puremagic.com/issues/show_bug.cgi?id=4846


Walter:

> A lot of it foundered on what the syntax for tuple literals should be. The top of the list is simply enclosing them in ( ).

This is a bad idea. It has caused troubles in Python because of the singleton syntax (tuple with 1 item).

One solution is to use a special unambigous delimiter to denote tuples, like (a similar solution is used in the Fortress language):

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


Otherwise a good solution is to use a name:

record()
record(1)
record(1, 2)
record(1, 2, 3)
record(1, 2, 3, 4)

I prefer the record() solution, but the first solution too acceptable.


> Finally, I got to thinking, why not just make it a special case:
> 
>   ( ) == tuple
>   (a) == parenthesized expression

This is not acceptable. No special cases, please. D has already a ton of special cases.
Python solves this with the (1,) syntax, but it's not nice, it's error-prone, and it confuses newbies.


> If expr represents a tuple, we (Andrei and I) were thinking about the syntax:
> 
>      auto (a, b, c, d) = expr;

On this topic I have this enhancement request: http://d.puremagic.com/issues/show_bug.cgi?id=4579


> The Lithpers among you will notice that this essentially provides a handy car,cdr shortcut for tuples and arrays:
> 
>      auto (car, cdr) = expr;

This is bad, it's not explicit enough. If you want to support this semantics then the syntax has to show what you mean. Python uses a * to denote "grab the whole tail". In D you may use something else, others have suggested tree points, this works with dynamic arrays too:

auto (car, cdr...) = expr;


Regarding field names for tuples, I have used Python and I like the optional names of D tuples (records). In some situations you don't need names, but in other situations field names are handy and help avoid bugs. In Python code that processes and uses tuples contains too many [0] [1] [2] etc that aren't readable and are bug-prone.

But a good management of such names asks for the names to not change the type of the tuple, this is why I talk about structural typing for records.

Bye,
bearophile
October 07, 2010
bearophile <bearophileHUGS@lycos.com> wrote:

> In my opinion it's good for a built-in D tuple to be a structural type. This also means you are allowed to perform an == among two tuples of different length (the result is known statically to be always false).

I understand (and agree to) the opinion that tuples should be structural
types, but why allow comparison of tuples of different lengths?


> Walter:
>
>> A lot of it foundered on what the syntax for tuple literals should be. The top
>> of the list is simply enclosing them in ( ).
>
> This is a bad idea. It has caused troubles in Python because of the singleton syntax (tuple with 1 item).
>
> One solution is to use a special unambigous delimiter to denote tuples, like (a similar solution is used in the Fortress language):

> (|1, 2, 3, 4|)
>
> Otherwise a good solution is to use a name:

> record(1, 2, 3, 4)
>
> I prefer the record() solution, but the first solution too acceptable.

Yeah, ( T... ) is not a good general tuple syntax. I believe auto( ) is
a free syntax in D, and thus could be used for tuples. Thinking more
about it, I am no longer as sure.


> auto (car, cdr...) = expr;

I really like this.


> Regarding field names for tuples, I have used Python and I like the optional names of D tuples (records). In some situations you don't need names, but in other situations field names are handy and help avoid bugs. In Python code that processes and uses tuples contains too many [0] [1] [2] etc that aren't readable and are bug-prone.
>
> But a good management of such names asks for the names to not change the type of the tuple, this is why I talk about structural typing for records.


I wrote a Tuple implementation for D that supports structural typing:

http://pastebin.com/qeYKa5GZ

(see line 58-60 for proof)
This is a simple proof-of-concept, so don't expect anything impressive
from it.


-- 
Simen
October 07, 2010
On 10/7/10 7:09 CDT, bearophile wrote:
> In the two threads (that are a single thread) most of the things I've seen are bad/wrong.
>
> I have discussed about Tuples several times in the D newsgroup and in Bugzilla. Please don't ignore all my work.
>
> Before designing tuple syntax you must decide what the purpose of D tuples is. Then you have to solve the design problems, and avoid all (or most) corner cases. In this discussion it's useful to have a certain experience of languages that use tuples often, as Python and others.

Good point.

> Tuples have some purposes:
> - Python, Go and other languages show that it's handy to allow functions to return multiple values, this means a tuple.
> - A handy tuple unpacking is useful at the calling point of a function that returns multiple return values.
> - Tuples are also useful as quick-and-dirty structs, to sort items in a different order, etc.
>
> It's useful to use [] to access tuple items, to slice tuples, concat them. It's useful for tuples to have a good textual representation, to be comparable lexicographically and to be hashable.

Yes, excellent. Now I realize we don't have hash for tuples just yet.

> Another design decision is if tuples have a nominative or structural type, this problem comes out in this bug report:
> http://d.puremagic.com/issues/show_bug.cgi?id=4128
>
> In my opinion it's good for a built-in D tuple to be a structural type. This also means you are allowed to perform an == among two tuples of different length (the result is known statically to be always false). I assume that D tuples know their length at compile-time.

Yah, I think tuples are the quintessential structural types. I think, however, that "==" shouldn't test for prefix (that would be _sub_typing). This is because slicing takes care of it. For example:

Tuple!(int, int, int) point3d;
Tuple!(int, int) point2d;
point2d == point3d; // doesn't compile
point2d == point3d[0 .. point2d.length]; // compiles

> Another significant problem is about naming things, currently the situation is a mess:
> http://www.digitalmars.com/d/archives/digitalmars/D/Tuple_TypeTuple_tupleof_etc_113005.html
> http://d.puremagic.com/issues/show_bug.cgi?id=4113
> In the end I have suggested to name "record" the typecons tuples, and "tuple" the typetuples.

I think we're in good shape with Tuple and tuple. The "other" tuples deserve an odder name.

> I have several bug reports and enhancement requests about tuples, please take them into account:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=4577
> http://d.puremagic.com/issues/show_bug.cgi?id=4582
> http://d.puremagic.com/issues/show_bug.cgi?id=4591
> http://d.puremagic.com/issues/show_bug.cgi?id=4666
> http://d.puremagic.com/issues/show_bug.cgi?id=4846

Nice. I like at least some of each.

> Walter:
>
>> A lot of it foundered on what the syntax for tuple literals should be. The top
>> of the list is simply enclosing them in ( ).
>
> This is a bad idea. It has caused troubles in Python because of the singleton syntax (tuple with 1 item).

During our conversation I conveyed my suspicion that that one corner case (which is very often encountered in generic code) will inevitably do this whole thing in, but he was quick to gloss over the issues. I'd be glad to have experience with Python save us some sweat. Do you have any links to discussions regarding the matter?

> One solution is to use a special unambigous delimiter to denote tuples, like (a similar solution is used in the Fortress language):
>
> (||)
> (|1|)
> (|1, 2|)
> (|1, 2, 3|)
> (|1, 2, 3, 4|)

Yup, the banana notation.

> Otherwise a good solution is to use a name:
>
> record()
> record(1)
> record(1, 2)
> record(1, 2, 3)
> record(1, 2, 3, 4)
>
> I prefer the record() solution, but the first solution too acceptable.

How about the shorter "tuple"? Wait, it's already there :o).

>> Finally, I got to thinking, why not just make it a special case:
>>
>>    ( ) == tuple
>>    (a) == parenthesized expression
>
> This is not acceptable. No special cases, please. D has already a ton of special cases.
> Python solves this with the (1,) syntax, but it's not nice, it's error-prone, and it confuses newbies.

Evidence please?

>> If expr represents a tuple, we (Andrei and I) were thinking about the syntax:
>>
>>       auto (a, b, c, d) = expr;
>
> On this topic I have this enhancement request:
> http://d.puremagic.com/issues/show_bug.cgi?id=4579
>
>
>> The Lithpers among you will notice that this essentially provides a handy
>> car,cdr shortcut for tuples and arrays:
>>
>>       auto (car, cdr) = expr;
>
> This is bad, it's not explicit enough. If you want to support this semantics then the syntax has to show what you mean. Python uses a * to denote "grab the whole tail". In D you may use something else, others have suggested tree points, this works with dynamic arrays too:
>
> auto (car, cdr...) = expr;

Nice.

> Regarding field names for tuples, I have used Python and I like the optional names of D tuples (records). In some situations you don't need names, but in other situations field names are handy and help avoid bugs. In Python code that processes and uses tuples contains too many [0] [1] [2] etc that aren't readable and are bug-prone.

Evidence please?

> But a good management of such names asks for the names to not change the type of the tuple, this is why I talk about structural typing for records.

Makes sense.


Andrei
October 07, 2010
2010/10/7 bearophile <bearophileHUGS@lycos.com>:
> Another design decision is if tuples have a nominative or structural type, this problem comes out in this bug report: http://d.puremagic.com/issues/show_bug.cgi?id=4128

> Another significant problem is about naming things, currently the situation is a mess:
> http://www.digitalmars.com/d/archives/digitalmars/D/Tuple_TypeTuple_tupleof_etc_113005.html
> http://d.puremagic.com/issues/show_bug.cgi?id=4113
> In the end I have suggested to name "record" the typecons tuples, and "tuple" the typetuples.

On these issues, I'm almost agreed with bearophile

I think we may not use 'Tuple' as 'a structure packed values'.
It is more better that 'Tuple' should *only* use as mixing sequence
types and values.

My proposals are:
1. We should name definitions of structures.
 - "Structure that all of fields have name" shuld be called 'Struct'.
 - "Structure that some of fields have name" shuld be called 'Odd struct'.
 - "Structure that none of fields have name" shuld be called 'Record'.

 Struct$B":(BOdd struct$B":(BRecord

2. We remove field namming funcion from std.typecons.tuple, and rename it to Record.

3. We rename std.typetuple.TypeTuple to Tuple.

--------
pseudo codes:

auto a = Record!(int, int)(10, 20);
auto b = Struct!(int, "x", int, "y")(100, 200);
//a = b; //should not compile, named field(x, y) cannot assign to unnamed field
b = a;   //should compile, unnamed field can assign to named field
assert(b[0] == 10);
assert(b[1] == 20);

auto c = OddStruct!(int, "x", int)(15, 25);
//a = c; //shuld not compile, named field(x) cannot assign to unnamed field
b = c;   //shuld compile
assert(b[0] == 15);
assert(b[1] == 25);
c = a;   //shuld compile
assert(c[0] == 10);
assert(c[1] == 20);

thanks.

Kenji Hara.
October 07, 2010
On 10/7/10 11:11 CDT, kenji hara wrote:
> 2010/10/7 bearophile<bearophileHUGS@lycos.com>:
>> Another design decision is if tuples have a nominative or structural type, this problem comes out in this bug report: http://d.puremagic.com/issues/show_bug.cgi?id=4128
> 
>> Another significant problem is about naming things, currently the situation is a mess:
>> http://www.digitalmars.com/d/archives/digitalmars/D/Tuple_TypeTuple_tupleof_etc_113005.html
>> http://d.puremagic.com/issues/show_bug.cgi?id=4113
>> In the end I have suggested to name "record" the typecons tuples, and "tuple" the typetuples.
> 
> On these issues, I'm almost agreed with bearophile
> 
> I think we may not use 'Tuple' as 'a structure packed values'.
> It is more better that 'Tuple' should *only* use as mixing sequence
> types and values.

The problem with this is that it departs from nomenclature that is agreed by everyone else, which is provincial.

First off, a tuple IS agreed to be an ordered collection of heterogeneous items. Google reveals copious evidence, both in math and programming language theory.

Benjamin Pierce's "Types and programming languages", a book that all PL students sleep with under their pillow, defines tuples in section 11.7 (entitled "Tuples") like D does. The first paragraph:

"It is easy to generalize the binary products of the previous section to n-ary products, often called tuples. For example, {1,2,true} is a 3-tuple containing two numbers and a Boolean. Its type is written {Nat,Nat,Bool}."

The following section defines records as tuples with labeled fields. I don't think it's a crime that D calls both tuples. We could define Record just to be more Catholic than the Pope, but I don't see a necessity there. At any rate, "Tuple" is correct, known, understood, and accepted.

D's built in type tuples (those used with TypeTuple) are weird. They are an artifact of the language that has no meaning outside it. Such tuples are defined as "anything that could be a template parameter", which really ties them to various language design decisions. My suggestion is that we deprecate TypeTuple and we call it AliasTuple because that's really what it is - it's a tuple of stuff that can be passed in as an alias parameter.

> My proposals are:
> 1. We should name definitions of structures.
>   - "Structure that all of fields have name" shuld be called 'Struct'.
>   - "Structure that some of fields have name" shuld be called 'Odd struct'.
>   - "Structure that none of fields have name" shuld be called 'Record'.
> 
>   Struct$B":(BOdd struct$B":(BRecord
> 
> 2. We remove field namming funcion from std.typecons.tuple, and rename it to Record.
> 
> 3. We rename std.typetuple.TypeTuple to Tuple.
> 
> --------
> pseudo codes:
> 
> auto a = Record!(int, int)(10, 20);

This is not a record by Pierce.

> auto b = Struct!(int, "x", int, "y")(100, 200);

This is a record by Pierce.

> auto c = OddStruct!(int, "x", int)(15, 25);

We could reject this during compilation if needed.

I don't see anything confusing grouping the above under "Tuple".


Andrei
October 07, 2010
2010$BG/(B10$B7n(B8$BF|(B1:34 Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>:
> On 10/7/10 11:11 CDT, kenji hara wrote:
>> 2010/10/7 bearophile<bearophileHUGS@lycos.com>:
>>> Another design decision is if tuples have a nominative or structural type, this problem comes out in this bug report: http://d.puremagic.com/issues/show_bug.cgi?id=4128
>>
>>> Another significant problem is about naming things, currently the situation is a mess:
>>> http://www.digitalmars.com/d/archives/digitalmars/D/Tuple_TypeTuple_tupleof_etc_113005.html
>>> http://d.puremagic.com/issues/show_bug.cgi?id=4113
>>> In the end I have suggested to name "record" the typecons tuples, and "tuple" the typetuples.
>>
>> On these issues, I'm almost agreed with bearophile
>>
>> I think we may not use 'Tuple' as 'a structure packed values'.
>> It is more better that 'Tuple' should *only* use as mixing sequence
>> types and values.
>
> The problem with this is that it departs from nomenclature that is agreed by everyone else, which is provincial.
>
> First off, a tuple IS agreed to be an ordered collection of heterogeneous items. Google reveals copious evidence, both in math and programming language theory.
>
> Benjamin Pierce's "Types and programming languages", a book that all PL students sleep with under their pillow, defines tuples in section 11.7 (entitled "Tuples") like D does. The first paragraph:
>
> "It is easy to generalize the binary products of the previous section to n-ary products, often called tuples. For example, {1,2,true} is a 3-tuple containing two numbers and a Boolean. Its type is written {Nat,Nat,Bool}."
>
> The following section defines records as tuples with labeled fields. I don't think it's a crime that D calls both tuples. We could define Record just to be more Catholic than the Pope, but I don't see a necessity there. At any rate, "Tuple" is correct, known, understood, and accepted.

I understood that 'Tuple' is a generic word in math/language theory. Withdraw my proposals.

> D's built in type tuples (those used with TypeTuple) are weird. They are an artifact of the language that has no meaning outside it. Such tuples are defined as "anything that could be a template parameter", which really ties them to various language design decisions. My suggestion is that we deprecate TypeTuple and we call it AliasTuple because that's really what it is - it's a tuple of stuff that can be passed in as an alias parameter.

It sounds for me that AliasTuple is a limited tuple contains only
alias parameters(exclude types).
I associate three kinds of template parameter (Type, Value, Alias)
with names '{Type|Value|Alias}Tuple'.
So I hope it will be called 'Tuple' in library, too.

(Given these, can I call std.typecons.Tuple ValueTyple?)

Thanks for your answer.

Kenji Hara
October 07, 2010
On 2010-10-07 12:34:33 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> My suggestion is that we deprecate TypeTuple and we call it AliasTuple because that's really what it is - it's a tuple of stuff that can be passed in as an alias parameter.

Personally, I like D built-in tuples; they're so simple. At the core they're just a group of "things". If you put only types in the tuple then it becomes usable as a type, and if you put only values in the tuple then it becomes usable as a value, and if I put variable declarations in the tuple then it becomes usable as a single variable aliased to all those variables, and if I mix all kind of things then it's just a heterogenous tuple that's probably only suitable as a template parameter.

Why should I know beforehand if I'm defining an alias tuple, a type tuple, a value tuple, or a whatever tuple? Seriously, the tuple is just a group of those "things" I put in it, and the compiler will tell me whenever I try to put that tuple where it doesn't belong.

Now, it sure would make sense to have a way to enforce whether a tuple is valid as a type or a valid as a value. But for many uses it isn't necessary, and it does simplify things to not have to care about it.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

October 07, 2010
Thu, 07 Oct 2010 13:45:16 -0400, Michel Fortin wrote:

> On 2010-10-07 12:34:33 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
> 
>> My suggestion is that we deprecate TypeTuple and we call it AliasTuple because that's really what it is - it's a tuple of stuff that can be passed in as an alias parameter.
> 
> Personally, I like D built-in tuples; they're so simple. At the core they're just a group of "things". If you put only types in the tuple then it becomes usable as a type, and if you put only values in the tuple then it becomes usable as a value, and if I put variable declarations in the tuple then it becomes usable as a single variable aliased to all those variables, and if I mix all kind of things then it's just a heterogenous tuple that's probably only suitable as a template parameter.
> 
> Why should I know beforehand if I'm defining an alias tuple, a type tuple, a value tuple, or a whatever tuple? Seriously, the tuple is just a group of those "things" I put in it, and the compiler will tell me whenever I try to put that tuple where it doesn't belong.
> 
> Now, it sure would make sense to have a way to enforce whether a tuple is valid as a type or a valid as a value. But for many uses it isn't necessary, and it does simplify things to not have to care about it.

We were discussing the semantics of the language. You can't design languages & compilers by just saying "do it the simple way, it should just work".

The tuples & proposals are already combining several different features: tuples, records, arrays (slices & indexing), varargs, type definitions (type tuples), dependent types, pattern matching, nominative & structural typing, and ad-hoc features (.tupleof which.. in fact isn't any of the three listed tuples). On top of that there are syntactical conflicts. How is that in any possible way simple?
October 07, 2010
On 10/7/10 12:45 CDT, Michel Fortin wrote:
> On 2010-10-07 12:34:33 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> said:
>
>> My suggestion is that we deprecate TypeTuple and we call it AliasTuple
>> because that's really what it is - it's a tuple of stuff that can be
>> passed in as an alias parameter.
>
> Personally, I like D built-in tuples; they're so simple. At the core
> they're just a group of "things".

They are terrible, awful, despiteful. They don't compose with anything; you can't have an array of tuples or a hash of tuples. They can't be returned a from a function. They spread their legs in function parameter lists without any control (flattening is bad, right?) Built-in tuples are the pitts. The one thing they're good for is as a back-end for std.typecons.Tuple.

> If you put only types in the tuple
> then it becomes usable as a type, and if you put only values in the
> tuple then it becomes usable as a value, and if I put variable
> declarations in the tuple then it becomes usable as a single variable
> aliased to all those variables, and if I mix all kind of things then
> it's just a heterogenous tuple that's probably only suitable as a
> template parameter.

Only a fraction of that is true. A tuple is not usable as a value.


Andrei
October 29, 2010
On 07/10/2010 19:45, Andrei Alexandrescu wrote:
> On 10/7/10 12:45 CDT, Michel Fortin wrote:
>> On 2010-10-07 12:34:33 -0400, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> said:
>>
>>> My suggestion is that we deprecate TypeTuple and we call it AliasTuple
>>> because that's really what it is - it's a tuple of stuff that can be
>>> passed in as an alias parameter.
>>
>> Personally, I like D built-in tuples; they're so simple. At the core
>> they're just a group of "things".
>
> They are terrible, awful, despiteful. They don't compose with anything;
> you can't have an array of tuples or a hash of tuples. They can't be
> returned a from a function. They spread their legs in function parameter
> lists without any control (flattening is bad, right?) Built-in tuples
> are the pitts. The one thing they're good for is as a back-end for
> std.typecons.Tuple.
>

In fairness, my impression is they were not meant to compose with anything or be returned with a function. They were created not as a first class type, but as a metaprogramming construct, whose purpose was *exactly* for capturing parameters for templates or functions and expanding them automatically. They were a great boon for D's metaprogramming capabilities.
As such they were not meant to emulate tuples as in Python's tuples, or any record type in general. But because they could partially be used as such, and because they share the same name, a lot of comparisons are made, which results in this idea that D's tuples are inferior.

This is not saying it would not be useful to have functionality like Python's tuples.

-- 
Bruno Medeiros - Software Engineer