Le 17/05/2012 16:20, Dario Schiavon a écrit :
Hi everybody!

I've been lurking this forum for quite some time, attracted by the power
and elegance of D. Although I'm not a programmer by profession, I often
happen to develop programs for scientific data evaluation at work and
I've always being intrigued with programming languages since I was a
teenager. A lot of time has passed since the last time I looked at D,
and now I'm really impressed with the amount of progress that has been
done.

However, I don't understand why some feature that would be so desirable
are still not implemented and, although there are already a lot of posts
about them, little to no progress is done at implementing them. One of
these is using tuples to return multiple values from functions. I'd like
to share with you my thoughts and opinions about how tuples might work
and be useful in D. I hope you find this contribution useful, and you
will let me understand where the problems are otherwise. I also
apologize for the length of my post if it doesn't really contain
anything new.

At the time being we have two kinds of tuples in D and a lot of
confusion about them. The first one is the Tuple object in Phobos
(std.typecons.Tuple), which I'm going to call "Record" for the rest of
the post to avoid confusion. They are pretty similar to Python's tuples,
except that they are not immutable and have named items (admittedly a
useful addition).

By introducing Records, you were probably trying to achieve the
following two points:

1) Provide a way to group values together, as if they were anonymous
struct's. Unfortunately, Records are not compatible with ordinary
struct's despite all their similarities. And I'm also not totally
convinced they are that useful, except when dealing with point 2. They
just confuse novices about whether they should be using Records or
struct's, just like they can't choose between tuples and lists in Python.

2) Provide a mechanism for functions to return multiple values. It may
be noted that functions in D can already return multiple values through
out/ref arguments, but most people agree that returning multiple values
would be a neater solution (I also do). This mechanism doesn't work yet
because of the lack of compiler support. I don't understand, however,
why Records should be a better candidate to implement this feature than
TypeTuples (more about that later).

Before going on, let me open a parenthesis about how returning multiple
values works in Python. Suppose that the function "func" returns two
values. The following saves the two values in the variables a and b.

(a, b) = func()

The parentheses around the tuple are not necessary, but I include them
anyway for clarity. I'd like you to notice that this syntax is treated
specially in Python. Python's tuples are immutable so you can't assign
values to them.

c = (0, 1)
c[0] = 2 # error: tuple object does not support item assignment
c = (2, 3) # this changes the reference c so that it points to a
different tuple

d = (a, b)
d = func() # this doesn't assign the return values to a and b!
(a, b) = func() # this does, but it's obviously a special case

Ok, enough for Python, let's go on with D.

The second kind of tuple is the in-built construct used in templates to
group template arguments (std.typetuple.TypeTuple). Let's call it
AliasTuple for the rest of the post, since TypeTuple is really a
misnomer (as others before me have already pointed out: they can contain
more than just types).

It must be noted that AliasTuples are not containers. They may be
considered a kind of compile-time container, but definitely not a
run-time container. With this, I mean that they don't copy their content
in a structured form in a determined region of memory at runtime, like
arrays, linked-lists and Records do. This implies, for example, that we
can't take their address or make an array of them. AliasTuples are just
collections of aliases, they don't contain actual data. So they are not
"just another struct-like container in the language", like Records are.

We may debate about how many defects AliasTuples have but, I guess, we
all agree that they are an extremely useful construct for D's templates.
Without them, templates in D would certainly be much more difficult to
use and they would lose much of their power. Therefore, I hope nobody
really intends to scrap them. If they have deficiencies (for instance,
they can't actually be used to return multiple values from functions), I
think we should improve them so that they cover all the useful use cases.

It is my opinion that AliasTuples are much more appropriate to manage
multiple return values than Records. However, for that to be possible,
we must solve some of their weaknesses. One of them is that there isn't
a concise literal expression yet. Let's suppose that we can create a
tuple like this:

@(1, 2, float) // equivalent to TypeTuple!(1, 2, float)

Of course it would be preferable to have just the parentheses without
the @. Unfortunately, it would clash with the normal parentheses and the
comma expression. In Python it works that way, but single-valued tuples
are awkwardly defined as (1,). Bearophile suggested for syntax (|1, 2|)
(although that was for Records), which I happen to like even less than
@(1, 2). Maybe someone will come out with a better syntax later on.

Since an AliasTuple contains just aliases, assigning a value to an item
of the AliasTuple is actually equivalent to assign the value to the
"thing" the alias points to. The following would be valid D code (it
already works if you replace @() with TypeTuple!()).

int a;
alias @(1, 2, float, a) b; // defines b to be the given tuple
b[3] = 3; // assigns 3 to the variable a

The following snipped, instead, doesn't work yet but I think it should
be made to work:

int a=1, b=2;
@(a, b) = @(b, a); // should swap values between a and b
writeln(a, b); // outputs "22" but should output "21"

Let's suppose it is possible to define a function that returns an
AliasTuple. The following might be a possible syntax. If the return type
of a function is an AliasTuple of only types, then the function should
returns an AliasTuple of values with those types.

@(int, int) func() {
return @(4, 5);
}
int x, y;
@(x, y) = func();

Note that the last line is not a special case as it is in Python. It
naturally works that way because an AliasTuple is a collection of
aliases and not a container. There should not be any implementation
problem in this feature, because it would be just syntax sugar for:

int func(out int z) {
z = 5;
return 4;
}
int x, y;
x = func(y);

What do you think of it?

I think you show a real need here, but I don't really like your proposal. I'd advocate for recycling the comma operator for tuple building.

This would be very similar to your proposal as a result, but no need to introduce a new syntax.

Reply via email to