Tuple DIP

Timon Gehr timon.gehr at gmx.ch
Wed Jan 17 21:10:47 UTC 2018


On 17.01.2018 20:43, Manu wrote:
> On 12 January 2018 at 14:44, Timon Gehr via Digitalmars-d 
> <digitalmars-d at puremagic.com <mailto:digitalmars-d at puremagic.com>> wrote:
> 
>     As promised [1], I have started setting up a DIP to improve tuple
>     ergonomics in D:
> 
>     https://github.com/tgehr/DIPs/blob/tuple-syntax/DIPs/DIP1xxx-tg.md
>     ...
> 
> 
> This is nice work!
> I hope this is seriously considered.
> 
> I'll add my 2c...
> You discuss 'auto unpacking', can you justify the value of this?
> ...

1. It exists already, and it seems inconsistent that it does not work 
for function calls:

import std.typecons, std.meta, std.stdio;
void main(){
     AliasSeq!(int,int) t = tuple(1, 2); // auto-expanded with alias this
     writeln(t); // 12
     int add(int a,int b){
         return a + b;
     }
     writeln(add(tuple(1, 2))); // why _not_ auto-expanded with alias this?
}


2. It's very useful in code that contains templates, for example ranges:

[(1, 2), (3, 4), (5, 6)].map!((a, b) => a + b);

At the moment, range code often gets a bit awkward, with indices all 
over the place, once tuples get involved, because the tuple components 
cannot be unpacked at the function call boundary.


3. I think it is a bit of a tragedy that the longest common substring of 
"(int a, int b) x;" and "void foo(int a, int b){}" does not have exactly 
the same interpretation for both, such that foo(x) is a perfect match. 
(The function declaration /looks like/ it declares a pattern that can be 
matched against by a tuple.) This cannot really be fixed, but 
auto-expansion removes the difference to the largest degree reasonably 
possible.


> I quite like C++ explicit unpacking (using ...), and I wouldn't be upset 
> to see that appear here too.
> Explicit unpacking would solve your breaking change with auto unpacking, 

It would also get rid of the feature I want. :-)

> but the buggest advantage of C++'s '...' statement is that the unpack 
> can involve expressions.
>    auto t = (1, 2, 3);
>    f(t...);          // <-- regular expansion: f(1, 2, 3);
>    f(arr[t]...);    // <-- expression expansion: f(arr[1], arr[2], arr[3]);
> etc...
> 
> It's amazingly useful to perform tuple expansion on an expression 
> involving the tuple!
> 
> So, why might implicit expansion be preferred to explicit expansion?

1. The code that calls the function might not be your own code. It could 
be code that is operating on some generic type T, which you happened to 
instantiate with a tuple. This code wouldn't really know when to expand 
if not checking explicitly on each call.


2. The particular solution C++ has chosen lacks a bit of flexibility: It 
cannot distinguish between zipping and taking a Cartesian product in 
case more than one tuple is involved.


3. There is not actually a trade-off. We already have explicit expansion 
(the .expand property). The additional built-in C++ functionality, and 
more, can be easily supported in the library with standard range-like 
syntax:

import std.range, std.string, std.algorithm, std.typecons, std.conv, 
std.stdio;

int f(int a, int b, int c){
     return a + b + c;
}

void main(){
     auto t = tuple(1, 2, 3);
     writeln(f(t.expand)); // 6
     auto arr = [0, 9, 4 ,3, 5];
     writeln(f(t.map!(i=>arr[i]).expand)); // 16
}


template map(alias f){
     auto map(T...)(Tuple!T t){
         return 
mixin(text("tuple(",std.algorithm.map!(i=>text("f(t[",i,"])"))(iota(t.length)).join(","),")"));
     }
}





More information about the Digitalmars-d mailing list