On 03/09/2012 05:14 PM, Manu wrote:
On 9 March 2012 17:57, Timon Gehr <timon.g...@gmx.ch
<mailto:timon.g...@gmx.ch>> wrote:
On 03/09/2012 04:38 PM, Manu wrote:
On 9 March 2012 16:27, Timon Gehr <timon.g...@gmx.ch
<mailto:timon.g...@gmx.ch>
<mailto:timon.g...@gmx.ch <mailto:timon.g...@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.