September 27, 2012
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.

September 27, 2012
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?
September 27, 2012
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
September 27, 2012
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
September 27, 2012
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.
September 27, 2012
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++;

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
September 27, 2012
> 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.

September 28, 2012
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.
September 28, 2012
> 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.
September 28, 2012
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 ?
6 7 8 9 10 11 12 13 14 15 16 17
Top | Discussion index | About this forum | D home