AliasTuples, rather than Records, to return multiple values
Dario Schiavon
dario.schiavon at gmail.com
Thu May 17 07:20:34 PDT 2012
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?
More information about the Digitalmars-d
mailing list