Multiple return values...
Timon Gehr
timon.gehr at gmx.ch
Fri Mar 9 10:16:19 PST 2012
On 03/09/2012 05:14 PM, Manu wrote:
> On 9 March 2012 17:57, Timon Gehr <timon.gehr at gmx.ch
> <mailto:timon.gehr at gmx.ch>> wrote:
>
> On 03/09/2012 04:38 PM, Manu wrote:
>
> On 9 March 2012 16:27, Timon Gehr <timon.gehr at gmx.ch
> <mailto:timon.gehr at gmx.ch>
> <mailto:timon.gehr at gmx.ch <mailto:timon.gehr at 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.
More information about the Digitalmars-d
mailing list