Thread overview
Tuple poilerplate code
Sep 01, 2020
JG
Sep 01, 2020
user1234
Sep 01, 2020
JG
Sep 01, 2020
Paul Backus
Sep 01, 2020
Simen Kjærås
Sep 01, 2020
WebFreak001
Sep 02, 2020
JG
Sep 19, 2020
JG
September 01, 2020
Is there anyway to remove the boilerplate code of dealing with tuples:

I find myself having to write things like this fairly often

auto someRandomName  = f(...); //where f returns a tuple with two parts
auto firstPart = someRandomName[0];
auto secondPart = someRandomName[1];


Is to possible to write something so that the above is essentially equivalent to:

assignTuple!(firstPart,secondPart) = f(...);

The closest I can produce is using a template mixin so that I would have to write:

mixin AssignTuple!(()=>f(...),"firstPart","secondPart");
September 01, 2020
On Tuesday, 1 September 2020 at 02:08:54 UTC, JG wrote:
> Is there anyway to remove the boilerplate code of dealing with tuples:
>
> I find myself having to write things like this fairly often
>
> auto someRandomName  = f(...); //where f returns a tuple with two parts
> auto firstPart = someRandomName[0];
> auto secondPart = someRandomName[1];
>
>
> Is to possible to write something so that the above is essentially equivalent to:
>
> assignTuple!(firstPart,secondPart) = f(...);
>
> The closest I can produce is using a template mixin so that I would have to write:
>
> mixin AssignTuple!(()=>f(...),"firstPart","secondPart");

---
void assignTuple(S, T...)(auto ref S s, auto ref T t)
{
    static foreach (i; 0 .. S.length)
        t[i] = s[i];
}

void main()
{
    import std;
    string a,b;
    tuple("a", "b").assignTuple(a,b);
}
---
September 01, 2020
On Tuesday, 1 September 2020 at 03:51:10 UTC, user1234 wrote:
> On Tuesday, 1 September 2020 at 02:08:54 UTC, JG wrote:
>> Is there anyway to remove the boilerplate code of dealing with tuples:
>>
>> I find myself having to write things like this fairly often
>>
>> auto someRandomName  = f(...); //where f returns a tuple with two parts
>> auto firstPart = someRandomName[0];
>> auto secondPart = someRandomName[1];
>>
>>
>> Is to possible to write something so that the above is essentially equivalent to:
>>
>> assignTuple!(firstPart,secondPart) = f(...);
>>
>> The closest I can produce is using a template mixin so that I would have to write:
>>
>> mixin AssignTuple!(()=>f(...),"firstPart","secondPart");
>
> ---
> void assignTuple(S, T...)(auto ref S s, auto ref T t)
> {
>     static foreach (i; 0 .. S.length)
>         t[i] = s[i];
> }
>
> void main()
> {
>     import std;
>     string a,b;
>     tuple("a", "b").assignTuple(a,b);
> }
> ---
Thanks for your answer. That helps somewhat, however it is still longer and less clear than one would ideally want. In addition you need to use explicit types.

September 01, 2020
On Tuesday, 1 September 2020 at 02:08:54 UTC, JG wrote:
> Is there anyway to remove the boilerplate code of dealing with tuples:
>
> I find myself having to write things like this fairly often
>
> auto someRandomName  = f(...); //where f returns a tuple with two parts
> auto firstPart = someRandomName[0];
> auto secondPart = someRandomName[1];

I like using the following snippet for this kind of thing:

    /// Pass the members of a tuple as arguments to a function
    template unpack(alias fun)
    {
        import std.typecons: isTuple;

        auto unpack(T)(T args)
            if (isTuple!T)
        {
            return fun(args.expand);
        }
    }

Usage looks like this:

    f(...).unpack!((firstPart, secondPart) {
        // use firstPart and secondPart in here
    });

It also works very well in range pipelines; for example,

    auto nums = [1, 2, 3];
    auto animals = ["lion", "tiger", "bear"];

    zip(nums, animals)
        .map!(unpack!((num, animal) => animal.repeat(num).joiner(" ")))
        .each!writeln;

...which prints the output:

    lion
    tiger tiger
    bear bear bear
September 01, 2020
On Tuesday, 1 September 2020 at 02:08:54 UTC, JG wrote:
> Is there anyway to remove the boilerplate code of dealing with tuples:
>
> I find myself having to write things like this fairly often
>
> auto someRandomName  = f(...); //where f returns a tuple with two parts
> auto firstPart = someRandomName[0];
> auto secondPart = someRandomName[1];
>
>
> Is to possible to write something so that the above is essentially equivalent to:
>
> assignTuple!(firstPart,secondPart) = f(...);
>
> The closest I can produce is using a template mixin so that I would have to write:
>
> mixin AssignTuple!(()=>f(...),"firstPart","secondPart");

When you know the types, this works:

    import std.typecons : tuple;
    import std.meta : AliasSeq;

    int firstPart;
    string secondPart;

    AliasSeq!(firstPart, secondPart) = tuple(1, "foo");

    assert(firstPart == 1);
    assert(secondPart == "foo");

I know Timon Gehr worked on a DIP for improved tuples, which I think would include the syntax `auto (firstPart, secondPart) = tuple(1, "foo");`, but I don't know what's happened to that idea lately.


I also feel it's worth pointing out that Paul Backus' code looks elegant when used outside a map as well:

tuple(1, "foo").unpack!((i, s) {
    writeln("i (", typeof(i).stringof, "): ", i,
          ", s (", typeof(s).stringof, "): ", s);
});

Will print:
i (int): 1, s (string): foo


--
  Simen
September 01, 2020
On Tuesday, 1 September 2020 at 02:08:54 UTC, JG wrote:
> [...]

Here is some fun with operator overloading and pointers, but I don't really like it because it seems unsafe:

import std;

auto _(T...)(return ref T refs) @safe {
    static struct Assigner(Ptrs...) {
        @disable this(this);
        private Ptrs ptrs;
        void opAssign(T)(T v) if (T.expand.length == Ptrs.length)
        {
            static foreach (i; 0 .. Ptrs.length)
                *ptrs[i] = v[i];
        }
    }

    static Assigner!U assigner(U...)(U v) {
        return Assigner!U(v);
    }

    static string assignerCall(size_t len)() {
    	string ret = "assigner(";
        static foreach (i; 0 .. len)
            ret ~= "&refs[" ~ i.stringof ~ "], ";
        return ret ~ ")";
    }

    return mixin(assignerCall!(T.length));
}

void main()
{
    string a, b, c;
    int x;
    _(a, b, c, x) = tuple("a", "b", "c", 4);
    writeln(a, b, c, x);
    _(a, b) = tuple(b, a);
    writeln(a, b);
}
September 02, 2020
Thank you all for the interesting suggestions.
September 19, 2020
On Wednesday, 2 September 2020 at 03:52:55 UTC, JG wrote:
> Thank you all for the interesting suggestions.

Still thinking about this from time to time.

Other than the suggestions given, this is what I have
been playing around with.

---------------------------------
import std.stdio;
import std.typecons : tuple;


mixin template assignTuple(alias vars, alias tupleFunc)
{
     import std.conv : to;
     auto tmp = tupleFunc();
     static foreach (i, var ; vars)
     {
          mixin("auto " ~ var ~ " = tmp[" ~ i.to!string ~ "];");
     }
}

auto f(int n) { return tuple(n, n+1, n+2, "A string here"); }

void main()
{
     mixin assignTuple!(["x","y","z", "str"],()=>f(3));
     writeln(x," ",y," ", z," \'",str,"\'");
}

---------------------------------
produces
---------------------------------
3 4 5 'A string here'
---------------------------------
I have a few questions:

1. Is the above code "bad" for some reason?
2. Is there a way of "hiding" tmp used in the mixin, so that it is not visible in main?