Better tuples
Philippe Sigaud
philippe.sigaud at gmail.com
Thu Jul 1 13:08:10 PDT 2010
On Thu, Jul 1, 2010 at 01:13, bearophile <bearophileHUGS at lycos.com> wrote:
> I'd like to add five enhancement requests in Bugzilla, related to
> std.typecons.Tuple. I show them here first for possible comments or
> critiques.
>
OK, here I go.
I don't plan to make you like current D tuples, but I'd like to show some
facilities the current syntax provide... and some things I discovered while
answering this, in case other people reading this didn't know them either.
I have seen that std.typecons.Tuple is quite useful, but it misses some very
> important features. Some features can just be added to it (see for example
> bug 4381 )
Yes this one (adding a .length member) seems easy.
just add either
@property size_t length() { return Types.length; }
or
immutable length = Types.length
> and maybe the apply(). But other features can't just be added, they need
> some syntax and semantic support.
>
> Some other useful things:
> - More integration and usage of Tuple in druntime. For example the
> associative array property AA.byItem() can yield tuple(key,value) lazily,
> and AA.items can return a dynamic array of them.
>
I like that, as the range associated to AA is quite naturally a lazy
Tuple!(K,V)[].
opSlice() is not defined for AA, so whe not use it to return a range?
auto range = aa[]; // lazily produce (K,V) pairs
> - Some way to reverse the order of the items of a tuple (generating a tuple
> of different type).
>
I had some fun with this and ideas like this. Inserting elements, rotating
tuples, reversing them, etc. Even mapping polymorphic functions on tuples
and reducing them, though I don't think these should be part of any standard
library.
You can these there:
http://svn.dsource.org/projects/dranges/trunk/dranges/docs/tuple2.html
- "in" operator for tuples (as for arrays).
>
Yes, and that seems easily implementable. I use this:
bool contains(U, T...)(Tuple!T tup, U elem)
{
static if (staticIndexOf!(U,T) == -1)
return false;
else
{
foreach(i, Type; tup.Types)
{
static if (is(Type == U))
if (tup.field[i] == elem) return true;
}
return false;
}
}
But I now see I could iterate directly by jumping at staticIndexOf(U,T) and
if the value is not OK, continuing on the tail. Though on most cases, tuples
have only a few elements...
> - More tuple-aware functions in Phobos, like a function to build an AA from
> a range of tuple(key, value).
>
Oh yes.
> - Optional: Zip and other pairing ranges to yield tuple (currently for
> efficiency they yield a pair of pointers. But this breaks abstraction. Using
> a more common data structure has significant advantages. The compiler can
> recognize the idiom and in some cases can avoid the creation of the Tuples
> that Zip produce.
>
I don't like the idea of compiler magic, but I do like the idea of having a
Zip that uses the standard structure: tuples. Its easier to inferface with
other functions this way. The Proxy from phobos zip is good for sorting
(which is why Andrei defined it this way, I guess), but it's a pain to use
otherwise, because it's a one-of-a-kind struct that cannot be fed to other
functions.
> Tuple unpacking is quite handy (the following syntax is just an
> syntax-idea, other syntaxes can be used. This syntax is not valid in C, so I
> think this syntax can be used in D):
>
> auto foo() {
> return tuple(10, "hello");
> }
> void main() {
> (int n, string s) = foo();
>
I also woud like that. While waiting to convince Walter, we can use some
poor man alternative like this one:
void copy(T...)(ref T to, Tuple!T from)
{
foreach(i,Type; T)
{
to[i] = from.field[i];
}
}
usage:
auto t = tuple(1, 3.14, "abc");
int i; double d; string s;
copy(i,d,s, t);
otherwise, you can use this:
auto ids = t.expand;
ids is an instantiated typetuple: you can index it (ids[1]), slice it
(ids[0..2]), iterate on it with foreach, etc.
The only thing you cannot do is returning it from a function ...
I know it's not as good as what you want (where you define and assign you n
and s in one go).
> writeln(n, " ", s);
> (n, s) = foo(); // calls is again
> int[] arr = [1, 2, 3];
> (int a, int b, int c) = arr; // other kind of unpacking
>
I experimented with a struct called RefTuple, that took constructor values
by ref and that did the kind of destructuring you present here. I created it
with a function called _ (yes, just _).
This gave the following syntax:
int n; string s;
_(n,s) = foo();
------------
>
> This is the syntax that can be currently used:
>
> auto foo() {
> T1 alpha = computeIt1(...);
> T1 alpha = computeIt2(...);
> return Tuple!(T1, "alpha", T2, "beta")(alpha, beta);
> //return tuple(alpha, beta); // alternative
> }
> void main() {
> auto alpha_beta = foo();
> use1(alpha_beta.alpha);
> use2(alpha_beta.beta);
> use1(alpha_beta.field[0]); // alternative
> use2(alpha_beta.field[1]); // alternative
> }
>
You can also use ._x:
use1(alpha_beta._0);
use1(alpha_beta._1);
>
> ------------
>
> More syntax that can be currently used:
>
> import std.stdio, std.typecons;
> void main() {
> int[] arr2 = [tuple(1, 2, 3).tupleof];
> writeln(arr2);
> }
>
> But it prints:
> 1 2 3 1 2 3
> Instead of:
> [1, 2, 3]
>
Yeah, tupleof return a strange value for tuple. I understood why, but
quickly forgot about it. Maybe it's possible to make .tupleof an alias of
.expand?
(** test, hmm no, doesn't work **)
I personnaly use expand a lot:
auto t = tuple(1,2,3);
int[] arr2 = [t.expand]; // works.
I like that to couple tuples and functions:
void foo(int i, double d, string s) {}
auto t = tuple(1, 3.14, "abc");
foo(t.expand); // works.
> opIndex/opIndexAssign syntax support for tuples:
>
> auto tup = tuple(10, 20, 30, 40);
> writeln(tup[0]);
> tup[1] = 5;
> int y = tup[2];
> tup[3]++;
>
>
> Slice syntax for tuples (currently done with 'Tuple.slice'):
> auto tup = tuple(10, 20, 30, 40, 50, 60);
> auto part = tup[1 .. 3];
>
Once again, you can use ._x, .field or .expand for that. It's not perfect,
but it's not bad either:
auto t = tuple(1, 3.14, "abc");
writeln(tup._0); // writes 1
t._1 = 1.414; // t is now tuple(1, 1.414, "abc")
string s = t._2;
t._0++; // works
alternative syntax (.field or .expand)
writeln(t.field[0]);
t.field[1] = 1.414;
string s = t.field[2];
t.field[0]++;
And for slicing:
auto t2 = t.field[1..3]; //but t is an instantiated typetuple (ie, an 'old
way' tuple, a (int,string), not a std.typecons.Tuple!(int,string))
auto t3 = tuple(t.field[1..3]); t3 is a bone fide Tuple!(double, string)
Man, typetuples are almost perfect... If only they could once again be
returned from functions...
>
> The type of tuple elements can differ, so the index must be known at
> compile-time, just as with tupleof[].
>
> A possible way to implement it (currently this can't be used):
> alias this.tupleof[0..$] this;
>
Tuple code list bug 2800 as a blocker for this.
>
> ==========================================
>
> Title: [Better tuples] (static) foreach on tuple items
>
> This is one of the tuple enhancement requests. See bug xxxx for an
> introduction.
>
> auto tup = tuple(10, 20, 30);
> static foreach (item; tup)
> writeln(item);
>
So, you can use:
foreach(index, item; tup.expand)
writeln(item);
(apply => smelting a tuple to give it to a function)
>
>
> A possible usage in D:
>
> void foo(T1, T2)(T1 x, T2 y) {
> writeln(x, " ", y);
> }
> void main() {
> auto tuple1 = tuple(1, 2);
> apply(&foo!(tuple1[0].typeof, tuple1[1].typeof), tuple1);
> auto tuple2 = tuple(1, 2, 3);
> apply(&foo!(tuple2[0].typeof, tuple2[1].typeof), tuple2[0..2]);
> }
>
>
Wheww.
OK, at the risk of repeating myself, why not like this?
void main() {
auto t = tuple(1,"abc");
foo(t.expand);
auto t2 = tuple(3.14, 1, "abc");
foo(t2.field[1..$]);
}
It's not that bad, no?
Philippe
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20100701/07e304b4/attachment.html>
More information about the Digitalmars-d
mailing list