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