October 07, 2010
Thu, 07 Oct 2010 18:56:46 +0000, retard wrote:

> Thu, 07 Oct 2010 13:48:31 -0500, Andrei Alexandrescu wrote:
> 
>> On 10/7/10 13:44 CDT, retard wrote:
>>> Thu, 07 Oct 2010 19:12:45 +0200, Simen kjaeraas wrote:
>>>
>>>> retard<re@tard.com.invalid>  wrote:
>>>>
>>>>> If you don't have first class tuple constructors and define them with a template, that's unfortunately not structural typing.
>>>>
>>>> Might I inquire as to why?
>>>
>>> I see these "tuples" defined with a template merely as structs in disguise. They don't cover all the cases where built-in first class tuples can be used.
>> 
>> Could you please take the time to put together a list of requirements? That would be great for either making Tuple better or for improving the language.
> 
> Sure, but I can't really promise to have to capacity to think about every possible case when we are discussing D. There are so many complex features! I think a more or less complete list can already be constructed from all past discussions here. The tuple issues come up every now and then.
> 
> FWIW, the library provided solution with syntactical support doesn't sound so bad now that I think about it. We just need to know, what we are trying to solve here. The bigger problems come up when overloading the same system with mixed value/type/alias tuples.

If I think about some basic features in other tuple using languages, you need to have a way to

1) construct tuples (as a statement and as an expression)

auto foo = (1,2);
return (1,2);

foo = { return (0, 42); }();

2) interact nicely with built-in types (AAs, structs, classes, arrays, nested tuples, and recursively all permutations of these come to mind)

3) extract a single field

auto foo = (1,2);
auto bar = foo._1;  // or foo[0]

4) extract all fields ("pattern matching")

auto baz = (1,2);
auto (foo, bar) = baz;

auto (foo2, _) = baz; // maybe even this? not necessary

5) tuples with named fields (beware conflicts with other literals)

auto baz = (foo: 1, bar: 2);

auto buz = baz.foo; // like this maybe?

6) since they use structural typing semantics, this should work

auto foo = (1,2);
auto bar = (2,3);

bar = foo;

---

I can't decide how the tuples should work with e.g. parameter lists. I'm not a big friend of auto-flattening on any level. E.g.

void foo((int,int) bar);

and

void foo(int a, int b);

should have a different type signature IMO.

These rules should cleanly generalize to mixed tuples in type context, when used in metaprogramming (e.g. parametric types).

People probably want to construct other literals such as arrays from tuples. These are all special cases from type system's pov. I have no opinion. Well I have -- I don't like messy rules.
October 07, 2010
On 10/7/10 13:56 CDT, retard wrote:
> Thu, 07 Oct 2010 13:48:31 -0500, Andrei Alexandrescu wrote:
>
>> On 10/7/10 13:44 CDT, retard wrote:
>>> Thu, 07 Oct 2010 19:12:45 +0200, Simen kjaeraas wrote:
>>>
>>>> retard<re@tard.com.invalid>   wrote:
>>>>
>>>>> If you don't have first class tuple constructors and define them with
>>>>> a template, that's unfortunately not structural typing.
>>>>
>>>> Might I inquire as to why?
>>>
>>> I see these "tuples" defined with a template merely as structs in
>>> disguise. They don't cover all the cases where built-in first class
>>> tuples can be used.
>>
>> Could you please take the time to put together a list of requirements?
>> That would be great for either making Tuple better or for improving the
>> language.
>
> Sure, but I can't really promise to have to capacity to think about every
> possible case when we are discussing D. There are so many complex
> features! I think a more or less complete list can already be constructed
> from all past discussions here. The tuple issues come up every now and
> then.

Yah, and it might be the case that we're too close to the matter sometimes. A simple out-of-the-box list should help. I'd start it like this:

- must contain an unlimited number of values, including non-distinct types etc.

- should be first-class values (e.g. array of tuples or returning tuples should require no tricks)

- should support controlled expansion, i.e. I decide when the tuple "explodes" whereas everywhere else it's a sheer value. Consider:

void foo(T)(T obj);
void foo(T, U)(T obj1, U obj2);
foo(tuple(1, 2.2));

Which function should be called? Any way we go, we should be able to call the first overload (because tuples are first-class values!!! the circle closes!!!)  but we should also be able to call the second overload if we so want. (By sheer luck, Tuple currently supports that beautifully: foo(tuple(1, 2.2)) calls the first overload and foo(tuple(1, 2.2).expand) calls the second one.)

- should support coherent conversion and perhaps subtyping, taking projections (slices), cross-product (concatenation)

- should be easy to use, terse, and self-explanatory

- should support simple "gathering" (assigning from various values) and "scattering" (assigning to various values)

What else?

> FWIW, the library provided solution with syntactical support doesn't
> sound so bad now that I think about it. We just need to know, what we are
> trying to solve here. The bigger problems come up when overloading the
> same system with mixed value/type/alias tuples.

I, too, have learned to appreciate library solutions more because I realized a few things. One, I really don't need to explain what this is:

auto x = tuple(1, 2.3);

or this

Tuple!(int, double) y;

or this

auto z = tuple("hello");

I mean it's as there as it gets. But I will need some explaining and some head scratching to figure this:

auto z = ("hello",);

and this:

auto t = ();

and this:

(int, double) foo(bool(int, double) bar);

Library tuples rule.


Andrei
October 07, 2010
Walter Bright:

> As has been proposed frequently, the , operator would have to be dispensed with.

This makes the -cstyle compiler switch more useful than before :-)

Bye,
bearophile
October 07, 2010
Thu, 07 Oct 2010 14:11:21 -0500, Andrei Alexandrescu wrote:

> On 10/7/10 13:56 CDT, retard wrote:
>> Thu, 07 Oct 2010 13:48:31 -0500, Andrei Alexandrescu wrote:
>>
>>> On 10/7/10 13:44 CDT, retard wrote:
>>>> Thu, 07 Oct 2010 19:12:45 +0200, Simen kjaeraas wrote:
>>>>
>>>>> retard<re@tard.com.invalid>   wrote:
>>>>>
>>>>>> If you don't have first class tuple constructors and define them with a template, that's unfortunately not structural typing.
>>>>>
>>>>> Might I inquire as to why?
>>>>
>>>> I see these "tuples" defined with a template merely as structs in disguise. They don't cover all the cases where built-in first class tuples can be used.
>>>
>>> Could you please take the time to put together a list of requirements? That would be great for either making Tuple better or for improving the language.
>>
>> Sure, but I can't really promise to have to capacity to think about every possible case when we are discussing D. There are so many complex features! I think a more or less complete list can already be constructed from all past discussions here. The tuple issues come up every now and then.
> 
> Yah, and it might be the case that we're too close to the matter sometimes. A simple out-of-the-box list should help. I'd start it like this:
> 
> - must contain an unlimited number of values, including non-distinct types etc.
> 
> - should be first-class values (e.g. array of tuples or returning tuples
> should require no tricks)
> 
> - should support controlled expansion, i.e. I decide when the tuple "explodes" whereas everywhere else it's a sheer value. Consider:
> 
> void foo(T)(T obj);
> void foo(T, U)(T obj1, U obj2);
> foo(tuple(1, 2.2));
> 
> Which function should be called? Any way we go, we should be able to
> call the first overload (because tuples are first-class values!!! the
> circle closes!!!)  but we should also be able to call the second
> overload if we so want. (By sheer luck, Tuple currently supports that
> beautifully: foo(tuple(1, 2.2)) calls the first overload and
> foo(tuple(1, 2.2).expand) calls the second one.)
> 
> - should support coherent conversion and perhaps subtyping, taking
> projections (slices), cross-product (concatenation)
> 
> - should be easy to use, terse, and self-explanatory
> 
> - should support simple "gathering" (assigning from various values) and
> "scattering" (assigning to various values)
> 
> What else?

Looks really good IMO.

The same .expand (or some other "method") should probably also work when dealing with tuples of types. I didn't look how it works now. The auto- flattening isn't always desirable. I guess the other places of confusion are interaction with built-in statements and other features, e.g.

auto foo = (1,2,3);

auto bar = [foo];

Should there be some way to construct a [1,2,3] array from the tuple or should the only be a way to construct arrays of the type tuple!(int)[] or something similar. The auto-flattening could be used in quite many places (as cases in switches etc.)
October 07, 2010
Andrei Alexandrescu napisał:

> On the client side the syntax is very light. The definition of the function would need to specify the type name:
> 
> Tuple!(int, "foo", string, "bar") fun(int) {
> ...
> }

Blue sky idea -- anonymous structs:

struct { int foo; string bar; } fun(int) {
...
}

They already exist but are allowed only as members of other structs/unions. Then again, it's still a language trip-up, like we don't have enough of those already.

> I think much of the list of grievances against tuple limitations stems from a feeling that doing tuples without special syntax is "cheating". For my money, library tuples are eminently usable, and once we fix a couple of compiler bugs, they will be as good as (if not simpler, richer, and clearer than) a built-in facility.

Amen.

> I do want to introduce
> syntax for expansion a la
> 
> auto (a, b) = foo(42);
>
> because that's a common need that's not satisfiable via a library.

Nice, but I can live without that.

> But
> then I want to define the syntax in a general way so it works not only
> with tuples, but also with arrays and types that implement opIndex.

There's a tangent issue with std.typecons.Tuple:

int[2] ints;
Tuple!(int, int) t = ints;

Minor, but should work.

-- 
Tomek
October 07, 2010
On 10/7/10 15:27 CDT, Tomek Sowiński wrote:
> There's a tangent issue with std.typecons.Tuple:
>
> int[2] ints;
> Tuple!(int, int) t = ints;
>
> Minor, but should work.

Yah, good point. Could you please add that to bugzilla so we don't forget? Feel free to assign to me (my first name at metalanguage dot con).

Andrei
October 07, 2010
retard <re@tard.com.invalid> wrote:
> I guess the other places of confusion
> are interaction with built-in statements and other features, e.g.
>
> auto foo = (1,2,3);
>
> auto bar = [foo];
>
> Should there be some way to construct a [1,2,3] array from the tuple or
> should the only be a way to construct arrays of the type tuple!(int)[] or
> something similar.

The obvious way to do this:

CommonType!T buildArray( T... )( T arg ) {
    return [ arg ];
}

auto foo = tuple( 1, 2, 3 );

auto bar = buildArray( foo.expand );

-- 
Simen
October 07, 2010
On 10/7/10 15:45 CDT, Simen kjaeraas wrote:
> retard <re@tard.com.invalid> wrote:
>> I guess the other places of confusion
>> are interaction with built-in statements and other features, e.g.
>>
>> auto foo = (1,2,3);
>>
>> auto bar = [foo];
>>
>> Should there be some way to construct a [1,2,3] array from the tuple or
>> should the only be a way to construct arrays of the type tuple!(int)[] or
>> something similar.
>
> The obvious way to do this:
>
> CommonType!T buildArray( T... )( T arg ) {
> return [ arg ];
> }
>
> auto foo = tuple( 1, 2, 3 );
>
> auto bar = buildArray( foo.expand );
>

Nice. Actually buildArray already exists - it's called array() and it sits in std.array.

Andrei
October 07, 2010
Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> I do want to introduce syntax for expansion a la
>
> auto (a, b) = foo(42);
>
> because that's a common need that's not satisfiable via a library. But then I want to define the syntax in a general way so it works not only with tuples, but also with arrays and types that implement opIndex.

Philippe Sigaud's reftuple[1] from dranges does that first part:

int a, b;
_(a,b) = tuple(b,a); // swap
_(a,b) = tuple(b,a+b); // fibonacci



[1]: http://svn.dsource.org/projects/dranges/trunk/dranges/docs/reftuple.html

-- 
Simen
October 07, 2010
Simen kjaeraas napisał:

> retard <re@tard.com.invalid> wrote:
>> I guess the other places of confusion
>> are interaction with built-in statements and other features, e.g.
>>
>> auto foo = (1,2,3);
>>
>> auto bar = [foo];
>>
>> Should there be some way to construct a [1,2,3] array from the tuple or should the only be a way to construct arrays of the type tuple!(int)[] or something similar.
> 
> The obvious way to do this:
> 
> CommonType!T buildArray( T... )( T arg ) {
>      return [ arg ];
> }
> 
> auto foo = tuple( 1, 2, 3 );
> 
> auto bar = buildArray( foo.expand );

From a different angle:

auto range = byFields(tuple(1,2,3));

It should take structs, classes, maybe unions. Of course, front() = 5 should update the source tuple. Feed it to std.(algorithm|range) for the win!

Also, I'd like to see two flavors:

byFields!(UnificationStrategy.CommonType)(...);  // default
byFields!(UnificationStrategy.Variant)(...);  // wrappers

-- 
Tomek