March 09, 2012
09.03.2012 2:23, Manu пишет:
> On 9 March 2012 01:56, Mantis <mail.mantis.88@gmail.com <mailto:mail.mantis.88@gmail.com>> wrote:
>
>     [...]
>     Is tuple required to be anonymous struct? I thought it's
>     implementation details that may be done the other way if tuples
>     implemented in language rather then library. There's another
>     problem with non-named return values, as this:
>     auto (sin_a, cos_a) = sincos( a );
>     is not equivalent to this:
>     auto (cos_a, sin_a) = sincos( a );
>
>
> I can't imagine a syntax that's non-destructive to the existing grammar where order is not important, but order would be clearly stated in the auto-complete pop-up, and in the reference. Also mismatching types would throw errors.

You can't mess with the parameters order in this case: http://dl.dropbox.com/u/36715190/Images/par_order.jpg
Is it impossible to make efficient multiple return values without the need to trade off the help from tools?
March 09, 2012
Manu:

> This is most certainly NOT a tuple-like thing. I don't think I can stress that point any harder :)

I understand. But in other languages tuples are used for such purpose, so there is some risk people will think of them as a third kind of tuples, despite they are a different thing.

What kind of syntax do you suggest?

Bye,
bearophile
March 09, 2012
On 9 March 2012 02:59, Mantis <mail.mantis.88@gmail.com> wrote:

> 09.03.2012 2:23, Manu пишет:
>
>> On 9 March 2012 01:56, Mantis <mail.mantis.88@gmail.com <mailto: mail.mantis.88@gmail.**com <mail.mantis.88@gmail.com>>> wrote:
>>
>>    [...]
>>
>>    Is tuple required to be anonymous struct? I thought it's
>>    implementation details that may be done the other way if tuples
>>    implemented in language rather then library. There's another
>>    problem with non-named return values, as this:
>>    auto (sin_a, cos_a) = sincos( a );
>>    is not equivalent to this:
>>    auto (cos_a, sin_a) = sincos( a );
>>
>>
>> I can't imagine a syntax that's non-destructive to the existing grammar where order is not important, but order would be clearly stated in the auto-complete pop-up, and in the reference. Also mismatching types would throw errors.
>>
>
> You can't mess with the parameters order in this case:
> http://dl.dropbox.com/u/**36715190/Images/par_order.jpg<http://dl.dropbox.com/u/36715190/Images/par_order.jpg>
> Is it impossible to make efficient multiple return values without the need
> to trade off the help from tools?
>

Eh? How so?

Visual studio show's the return value and function name in the same tooltip
(not just the argument list as your image shows). Tools may need to tweak
their popup if they don't present the return values already.
Also, tools can do the usual red-squiggly underline thing on return
assignments with mismatching result count or types easily.


On 9 March 2012 03:24, bearophile <bearophileHUGS@lycos.com> wrote:

> I understand. But in other languages tuples are used for such purpose, so there is some risk people will think of them as a third kind of tuples, despite they are a different thing.
>

I really don't think so. The only language I know of that behaves like you
say is lua (oh, and python?); scripting languages, not too interested with
ABI expression, and bear virtually no relation whatsoever to any c-like
language.
I think the comparison might be drawn to something more like Go. Octave
does it with an interesting syntax, but these are not compatible with D in
a non-breaking way.

What kind of syntax do you suggest?
>

I detailed my initial idea in my post prior to yours, but perhaps other more interesting syntax might also exist:

I imagine something like:
> auto (x, y) = func(); // specify auto for all results?
> float (x, y) = func(); // specify explicit type for all results?
> (int x, float y) = func; // explicitly type each result?
> int x; ... (x, float y) = func(); // assign to predeclared variable(/s)?
> (x, , z) = func(); // ignore the second result value, intuitive and
> visible (elimination of the second result's code path)
>
> I'm sure other more bizarre syntax could be possible too to help reduce bracket spam. Ideas from things like lambda syntax?
>


March 09, 2012
On 03/09/2012 01:23 AM, Manu wrote:
> I can imagine syntax using parentheses, but I don't think I'm qualified
> to propose a robust syntax, I don't know enough about the finer details
> of the grammar.
> Perhaps if other people agree with me, they could present some creative
> solutions to the syntax?
>
> I imagine something like:
> auto (x, y) = func(); // specify auto for all results?
> float (x, y) = func(); // specify explicit type for all results?
> (int x, float y) = func; // explicitly type each result?

This works, and Kenji Hara has already implemented appropriate parser extensions.

> int x; ... (x, float y) = func(); // assign to predeclared variable(/s)?
> (x, , z) = func(); // ignore the second result value (elimination of the
> second result's code path)
>

Those two would work, but (x,y) = func(); conflicts with the comma operator. (I'd prefer (,) to be a tuple constructor though.)

March 09, 2012
On Fri, Mar 09, 2012 at 03:27:14PM +0100, Timon Gehr wrote:
> On 03/09/2012 01:23 AM, Manu wrote:
[...]
> >int x; ... (x, float y) = func(); // assign to predeclared variable(/s)?
> >(x, , z) = func(); // ignore the second result value (elimination of the
> >second result's code path)
> >
> 
> Those two would work, but (x,y) = func(); conflicts with the comma
> operator. (I'd prefer (,) to be a tuple constructor though.)

Just out of curiosity, *why* does D have a comma operator? It's one of those obscure things about C that can be really, really, nasty if you're unaware of it. And C++ makes it worse by making the comma operator *overloadable*.


T

-- 
It's amazing how careful choice of punctuation can leave you hanging:
March 09, 2012
On 9 March 2012 16:27, Timon Gehr <timon.gehr@gmx.ch> wrote:

> On 03/09/2012 01:23 AM, Manu wrote:
>
>> I can imagine syntax using parentheses, but I don't think I'm qualified
>> to propose a robust syntax, I don't know enough about the finer details
>> of the grammar.
>> Perhaps if other people agree with me, they could present some creative
>> solutions to the syntax?
>>
>> I imagine something like:
>> auto (x, y) = func(); // specify auto for all results?
>> float (x, y) = func(); // specify explicit type for all results?
>> (int x, float y) = func; // explicitly type each result?
>>
>
> This works, and Kenji Hara has already implemented appropriate parser extensions.
>
>  int x; ... (x, float y) = func(); // assign to predeclared variable(/s)?
>> (x, , z) = func(); // ignore the second result value (elimination of the
>>
>> second result's code path)
>>
>>
> Those two would work, but (x,y) = func(); conflicts with the comma
> operator. (I'd prefer (,) to be a tuple constructor though.)
>

You think so? Within that context, I would think the coma could be reinterpreted however it likes. The usual use of the coma operator makes no sense in this context?

These last 2 examples are what I see as being the most important part, and
the precise reason that it SHOULDN'T be a tuple. The ability to directly
assign results to explicit (existing) variables, and to ignore some/all of
the return values, is a fundamental feature of the *concept* of return
values universally. I see this as basically the whole point.
Another example: (someStruct.x, y, , int err) = func();
In this example, I assign the x result to a struct, y assigns to some
existing local, we ignore z because we can (visually states our intent,
would be hidden through use of a tuple), and we declare an int to capture a
potential error in place.
If we were abusing the tuple syntax, we would need additional lines
following the call to assign the rvalues out to their appropriate places,
which is unnecessary spaghetti.


March 09, 2012
On Mar 9, 2012 10:28 AM, "H. S. Teoh" <hsteoh@quickfur.ath.cx> wrote:
>
> On Fri, Mar 09, 2012 at 03:27:14PM +0100, Timon Gehr wrote:
> > On 03/09/2012 01:23 AM, Manu wrote:
> [...]
> > >int x; ... (x, float y) = func(); // assign to predeclared
variable(/s)?
> > >(x, , z) = func(); // ignore the second result value (elimination of
the
> > >second result's code path)
> > >
> >
> > Those two would work, but (x,y) = func(); conflicts with the comma
> > operator. (I'd prefer (,) to be a tuple constructor though.)
>
> Just out of curiosity, *why* does D have a comma operator? It's one of those obscure things about C that can be really, really, nasty if you're unaware of it. And C++ makes it worse by making the comma operator *overloadable

The comma operator can be worked around by using braces instead.  Of course it is very breaking and quite arguably ugly.

for ({int I; float j;} ; ) ;

But then you have to worry about what it returns.  The sequence operator is actually useful it is just unfortunate that they used it for function parameters also.

I think that the best work around is either the braces which can be quite elegant because it fits the rest of the language and say it returns the return value of the last statement.  Or pick a new character for the sequence operator.

This is actually kinds nice because you get "tuple" in "tuple" out.  Of course they don't need to be tuples that can be passed around they can be values that must be unpacked right away.


March 09, 2012
On 03/09/2012 04:38 PM, Manu wrote:
> On 9 March 2012 16:27, Timon Gehr <timon.gehr@gmx.ch
> <mailto:timon.gehr@gmx.ch>> wrote:
>
>     On 03/09/2012 01:23 AM, Manu wrote:
>
>         I can imagine syntax using parentheses, but I don't think I'm
>         qualified
>         to propose a robust syntax, I don't know enough about the finer
>         details
>         of the grammar.
>         Perhaps if other people agree with me, they could present some
>         creative
>         solutions to the syntax?
>
>         I imagine something like:
>         auto (x, y) = func(); // specify auto for all results?
>         float (x, y) = func(); // specify explicit type for all results?
>         (int x, float y) = func; // explicitly type each result?
>
>
>     This works, and Kenji Hara has already implemented appropriate
>     parser extensions.
>
>         int x; ... (x, float y) = func(); // assign to predeclared
>         variable(/s)?
>         (x, , z) = func(); // ignore the second result value
>         (elimination of the
>
>         second result's code path)
>
>
>     Those two would work, but (x,y) = func(); conflicts with the comma
>     operator. (I'd prefer (,) to be a tuple constructor though.)
>
>
> You think so? Within that context, I would think the coma could be
> reinterpreted however it likes. The usual use of the coma operator makes
> no sense in this context?

void main(){
    int a,b;
    (a,b)=2;
    assert(a==0);
    assert(b==2);
}

>
> These last 2 examples are what I see as being the most important part,
> and the precise reason that it SHOULDN'T be a tuple.

You are probably confusing the tuple concept with a Phobos Tuple.

> The ability to
> directly assign results to explicit (existing) variables, and to ignore
> some/all of the return values, is a fundamental feature of the /concept/
> of return values universally.
> I see this as basically the whole point.
> Another example: (someStruct.x, y, , int err) = func();
> In this example, I assign the x result to a struct, y assigns to some
> existing local, we ignore z because we can (visually states our intent,
> would be hidden through use of a tuple), and we declare an int to
> capture a potential error in place.

This is simple pattern matching.

> If we were abusing the tuple syntax, we would need additional lines
> following the call to assign the rvalues out to their appropriate
> places, which is unnecessary spaghetti.

What you propose is tuple syntax.


March 09, 2012
On 9 March 2012 17:57, Timon Gehr <timon.gehr@gmx.ch> wrote:

> On 03/09/2012 04:38 PM, Manu wrote:
>
>> On 9 March 2012 16:27, Timon Gehr <timon.gehr@gmx.ch <mailto:timon.gehr@gmx.ch>> wrote:
>>
>>    On 03/09/2012 01:23 AM, Manu wrote:
>>
>>        I can imagine syntax using parentheses, but I don't think I'm
>>        qualified
>>        to propose a robust syntax, I don't know enough about the finer
>>        details
>>        of the grammar.
>>        Perhaps if other people agree with me, they could present some
>>        creative
>>        solutions to the syntax?
>>
>>        I imagine something like:
>>        auto (x, y) = func(); // specify auto for all results?
>>        float (x, y) = func(); // specify explicit type for all results?
>>        (int x, float y) = func; // explicitly type each result?
>>
>>
>>    This works, and Kenji Hara has already implemented appropriate
>>    parser extensions.
>>
>>        int x; ... (x, float y) = func(); // assign to predeclared
>>        variable(/s)?
>>        (x, , z) = func(); // ignore the second result value
>>        (elimination of the
>>
>>        second result's code path)
>>
>>
>>    Those two would work, but (x,y) = func(); conflicts with the comma
>>    operator. (I'd prefer (,) to be a tuple constructor though.)
>>
>>
>> You think so? Within that context, I would think the coma could be reinterpreted however it likes. The usual use of the coma operator makes no sense in this context?
>>
>
> void main(){
>    int a,b;
>    (a,b)=2;
>    assert(a==0);
>    assert(b==2);
>
> }
>
>
>> These last 2 examples are what I see as being the most important part, and the precise reason that it SHOULDN'T be a tuple.
>>
>
> You are probably confusing the tuple concept with a Phobos Tuple.
>
>  The ability to
>> directly assign results to explicit (existing) variables, and to ignore some/all of the return values, is a fundamental feature of the /concept/
>>
>> of return values universally.
>> I see this as basically the whole point.
>> Another example: (someStruct.x, y, , int err) = func();
>> In this example, I assign the x result to a struct, y assigns to some
>> existing local, we ignore z because we can (visually states our intent,
>> would be hidden through use of a tuple), and we declare an int to
>> capture a potential error in place.
>>
>
> This is simple pattern matching.


I'm not sure what you mean by this?


> If we were abusing the tuple syntax, we would need additional lines
>> following the call to assign the rvalues out to their appropriate places, which is unnecessary spaghetti.
>>
>
> What you propose is tuple syntax.
>

What I mean is this:

retTuple = func();
someStruct.x = retTuple[0];
y = retTuple[1];
// retTuple[2] is ignored, but the intent is not clear in the code as it
was in my prior example, I like how my prior example makes this intent
explicit
int err = retTuple[3];

This is pretty horrible. Surely you can see why I want to be able to
arbitrarily assign the return values directly?
That's what I mean by 'abuse of the tuple syntax', but if that's not what
you mean, then show me an example of the usage of your suggestion?


March 09, 2012
On 03/09/2012 05:14 PM, Manu wrote:
> On 9 March 2012 17:57, Timon Gehr <timon.gehr@gmx.ch
> <mailto:timon.gehr@gmx.ch>> wrote:
>
>     On 03/09/2012 04:38 PM, Manu wrote:
>
>         On 9 March 2012 16:27, Timon Gehr <timon.gehr@gmx.ch
>         <mailto:timon.gehr@gmx.ch>
>         <mailto:timon.gehr@gmx.ch <mailto:timon.gehr@gmx.ch>>> wrote:
>
>             On 03/09/2012 01:23 AM, Manu wrote:
>
>                 I can imagine syntax using parentheses, but I don't
>         think I'm
>                 qualified
>                 to propose a robust syntax, I don't know enough about
>         the finer
>                 details
>                 of the grammar.
>                 Perhaps if other people agree with me, they could
>         present some
>                 creative
>                 solutions to the syntax?
>
>                 I imagine something like:
>                 auto (x, y) = func(); // specify auto for all results?
>                 float (x, y) = func(); // specify explicit type for all
>         results?
>                 (int x, float y) = func; // explicitly type each result?
>
>
>             This works, and Kenji Hara has already implemented appropriate
>             parser extensions.
>
>                 int x; ... (x, float y) = func(); // assign to predeclared
>                 variable(/s)?
>                 (x, , z) = func(); // ignore the second result value
>                 (elimination of the
>
>                 second result's code path)
>
>
>             Those two would work, but (x,y) = func(); conflicts with the
>         comma
>             operator. (I'd prefer (,) to be a tuple constructor though.)
>
>
>         You think so? Within that context, I would think the coma could be
>         reinterpreted however it likes. The usual use of the coma
>         operator makes
>         no sense in this context?
>
>
>     void main(){
>         int a,b;
>         (a,b)=2;
>         assert(a==0);
>         assert(b==2);
>
>     }
>
>
>         These last 2 examples are what I see as being the most important
>         part,
>         and the precise reason that it SHOULDN'T be a tuple.
>
>
>     You are probably confusing the tuple concept with a Phobos Tuple.
>
>         The ability to
>         directly assign results to explicit (existing) variables, and to
>         ignore
>         some/all of the return values, is a fundamental feature of the
>         /concept/
>
>         of return values universally.
>         I see this as basically the whole point.
>         Another example: (someStruct.x, y, , int err) = func();
>         In this example, I assign the x result to a struct, y assigns to
>         some
>         existing local, we ignore z because we can (visually states our
>         intent,
>         would be hidden through use of a tuple), and we declare an int to
>         capture a potential error in place.
>
>
>     This is simple pattern matching.
>
>
> I'm not sure what you mean by this?
>
>         If we were abusing the tuple syntax, we would need additional lines
>         following the call to assign the rvalues out to their appropriate
>         places, which is unnecessary spaghetti.
>
>
>     What you propose is tuple syntax.
>
>
> What I mean is this:
>
> retTuple = func();
> someStruct.x = retTuple[0];
> y = retTuple[1];
> // retTuple[2] is ignored, but the intent is not clear in the code as it
> was in my prior example, I like how my prior example makes this intent
> explicit
> int err = retTuple[3];
>
> This is pretty horrible. Surely you can see why I want to be able to
> arbitrarily assign the return values directly?
> That's what I mean by 'abuse of the tuple syntax', but if that's not
> what you mean, then show me an example of the usage of your suggestion?

There are two parts, syntax and semantics.

Semantics:
D is already able to express those:

template Tuple(T...){alias T Tuple;} // not the same as std.typecons.Tuple!

// function with multiple return values:
Tuple!(int,double) foo(int a, double b){
    Tuple!(int, double) result; // ok, _no imposed memory layout_
    result[0] = a;              // ok
    result[1] = a+b;              // ok
    return result;
}

Multiple return values are currently *disallowed explicitly*:
DMD sez: "Error: functions cannot return a tuple"

Just specify the ABI, implement the code gen, and we're done.

Moot point: built-in tuples auto-flatten inside comma-separated lists.

std.typecons.Tuple is a hack to circumvent the arbitrary "cannot return tuple from function" restriction as well as the auto-flattening. The problem is that it is implemented as a struct with a built-in tuple member. The fact that it is a struct imposes a memory layout. This is just a side-effect of attempting to address the other two issues. It is not something that is desirable.


Syntax:
Currently, there is just none. Tuples are a built-in types that cannot be created without a template that makes them accessible.

IMHO Ideally, it would look like this:


(int, double) foo(int a, double b) => (a, a+b);//Jonathan does not like this

void main(){
    (int, double) bar = (1,2.0);
    auto (x,y) = (1,2.0);
    bar = foo(x,y);
    (x,_) = foo(bar); // ignore y, assign x (bar auto-flattened)
    (_,y) = foo(bar); // ignore x, assign y (bar auto-flattened)

    x = 1, y = 2.0; // comma operator
    auto z = (x = 1, y = 2.0)[$-1]; // "comma operator"

    (int x, double y) foo = (1,2.0); // name the tuple fields
    static assert(is(typeof(foo.x==int)));
    static assert(is(typeof(foo.y==double)));

    bar = foo; // structural typing of tuples, field names don't matter
    foo = bar;

    (foo, bar) = (1, 2.0, 3, 4.0); // foo and bar auto-flattened
    assert(foo.x == 1 && foo.y == 2.0 && bar[0]==3 && bar[1] == 4.0);

    MultiRange range; // a range with multiple element types, eg (int, double) front(){...}
    foreach((a,b); range){ ... }
}

But this would break existing code that uses the comma operator...
Another issue is that people would complain about auto-flattening all the time once built-in tuples get more accessible, even though it is not actually a problem. It would be just due to the fact that it does not occur in most other popular programming languages.