www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - std.algorithm.map with multiple lambdas (2.066.0-b1) (does not

reply "klasbo" <klasbo gmail.com> writes:
   void main(){
       import std.algorithm, std.stdio;
       auto arr = [1,2,3];
       arr.map!("a + a", "a * a").writeln;  //compiles
       arr.map!(a => a + a, a => a * a).writeln; //does not
   }


If I define two functions outside main, it works:


   void main(){
       import std.algorithm, std.stdio;
       auto arr = [1,2,3];
       arr.map!(twoTimes, square).writeln;
   }
   int square(int i){ return i*i; }
   int twoTimes(int i){ return i+i; }


However, if `twoTimes` and `square` are nested inside of main(), 
it fails.

[...]\typetuple.d(550): Error: template instance F!(twoTimes) 
cannot use local 'twoTimes' as parameter to non-global template 
AppliedReturnType(alias f)
[...]\typetuple.d(556): Error: template instance 
maptest.main.staticMap!(AppliedReturnType, twoTimes) error 
instantiating
[...]\algorithm.d(415):        instantiated from here: 
staticMap!(AppliedReturnType, twoTimes, square)
.\maptest.d(8):        instantiated from here: map!(int[])

The alias declaration
`alias AppliedReturnType(alias f) = typeof(f(r.front));`
... is something I have never seen before.

So I hardcoded/expanded this line:
`alias ReturnTypes = TypeTuple!(AppliedReturnType!(_funs[0]), 
AppliedReturnType!(_funs[1]));`
... which gives me the same error (cannot use local as parameter 
to non-global). So I threw it in a pragma(msg, [...]):
`pragma(msg, TypeTuple!(AppliedReturnType!(_funs[0]), 
AppliedReturnType!(_funs[1])));`
... which printed `(int, int)`, as expected. And also the same 
error.

Wat.
Jul 05 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
This is an instance of these bugs:

https://issues.dlang.org/show_bug.cgi?id=5710
https://issues.dlang.org/show_bug.cgi?id=11946

But seeing that `map` works with a single lambda/local function, 
it should be possible to make it work with several ones too.

For the time being, a simple workaround is to make the local 
functions static:

void main() {
     static int square(int i) { return i*i; }
     static int twoTimes(int i) { return i+i; }
     import std.algorithm, std.stdio;
     auto arr = [1,2,3];
     arr.map!(twoTimes, square).writeln;
}
Jul 05 2014
parent "klasbo" <klasbo gmail.com> writes:
On Saturday, 5 July 2014 at 19:31:24 UTC, Marc Schütz wrote:
 This is an instance of these bugs:

 https://issues.dlang.org/show_bug.cgi?id=5710
 https://issues.dlang.org/show_bug.cgi?id=11946

 But seeing that `map` works with a single lambda/local 
 function, it should be possible to make it work with several 
 ones too.

 For the time being, a simple workaround is to make the local 
 functions static:
This problem was introduced when std.algorithm.map was made to refuse void functions. But map already did fail void when it gets multiple functions, because std.typecons.tuple refuses variables of type void (though the error message is cryptic, sure): "Error: variable std.typecons.Tuple!void.Tuple._expand_field_0 variables cannot be of type void" The only thing map didn't fail was single argument void. This was fixed, so why not re-use the same fix for multiple arguments? The example below works fine, and gives the new and sensible-er error message: template map(fun...) if (fun.length >= 1) { auto map(Range)(Range r) if (isInputRange!(Unqual!Range)) { alias AppliedReturnType(alias f) = typeof(f(r.front)); static if (fun.length > 1) { import std.functional : adjoin; import std.typetuple : staticIndexOf; alias _funs = staticMap!(unaryFun, fun); alias _fun = adjoin!_funs; // Attack of the copy-paste and poorly chosen variable name: foreach(_f; _funs){ static assert(!is(AppliedReturnType!_f == void), "All mapping functions must not return void."); } } else { alias _fun = unaryFun!fun; static assert(!is(AppliedReturnType!_fun == void), "Mapping function must not return void."); } return MapResult!(_fun, Range)(r); } } Am I missing something?
Jul 05 2014