We need to have a way to say "convert this nested function into a struct"

John Colvin via Digitalmars-d digitalmars-d at puremagic.com
Wed Oct 28 04:42:06 PDT 2015


On Saturday, 6 June 2015 at 12:49:37 UTC, Atila Neves wrote:
> On Saturday, 6 June 2015 at 06:59:26 UTC, Jonathan M Davis 
> wrote:
>> On Saturday, 6 June 2015 at 06:16:17 UTC, Andrei Alexandrescu 
>> wrote:
>>> Nested functions that allocate their environment dynamically 
>>> can be quite useful. However, oftentimes the need is to 
>>> convert the code plus the data needed into an anonymous 
>>> struct that copies the state inside, similar to C++ lambdas 
>>> that capture by value.
>>>
>>> I wonder how to integrate that within the language nicely.
>>
>> Some of us were discussing this at dconf. Essentially, we need 
>> a way to create a functor similar to how C++ lambdas do. The 
>> most straightforward way would involve string mixins, and 
>> you'd do something like
>>
>> auto f = makeFunctor!"function code here"(arguments);
>> auto result = range.algorithm!f();
>>
>> but that's not terribly pretty. Atila seemed to have figured 
>> out how we could do it with std.functional.partial, but I was 
>> too tired at the time to quite understand what his proposal 
>> was. So, we may have something better there. Ideally, we'd be 
>> able to just give a lambda, but that would put us right back 
>> in the problem of a delegate being allocated unnecessarily 
>> (though IIRC, Atila's suggestion somehow worked with lambdas 
>> and partial without allocating; I wish that I could remember 
>> what he proposed). But while it may or not be as pretty as 
>> we'd like, I think that it's at last _possible_ for us to have 
>> a shorthand for creating a functor by just providing the 
>> function's body and arguments that hold the values for its 
>> members. I'm certainly not against finding a language way to 
>> make it prettier though, since I'm not sure how clean we can 
>> really do it without language help.
>>
>> That being said, we really should find a way to make it so 
>> that lambda's don't turn into delegates unless they really 
>> need to. In many, many cases, they should be plenty efficient 
>> without having to force the issue with functors, but they 
>> aren't, because we allocate for them unnecessarily. I don't 
>> know how easy it'll be though for the compiler devs to figure 
>> out how to optimize that, since sometimes you _do_ need to 
>> allocate a closure.
>>
>> But having a shorthand way to create functors would definitely 
>> allow us to force the issue where necessary. And from what 
>> Liran was saying at dconf, that alone would make it possible 
>> for them to use a lot of Phobos that they can't right now. I 
>> suspect that unnecessary closures are actually the main reason 
>> that we have GC allocation problems with Phobos, since most 
>> algorithms just don't explicitly involve allocation unless 
>> they're doing array-specific stuff.
>>
>> - Jonathan M Davis
>
> I remember the conversation but not really what I said. 
> However, I just wrote this:
>
>
> import std.stdio;
> import std.algorithm;
> import std.range;
> import std.conv;
> import std.traits;
> import std.exception;
>
>
> auto functorPartial(alias F, T)(T arg) {
>     struct Functor {
>
>         T arg;
>
>         this(T args) { //because of opCall
>             this.arg = arg;
>         }
>
>         auto opCall(U...)(U rest) {
>             return F(arg, rest);
>         }
>     }
>
>     return Functor(arg);
> }
>
> int adder(int i, int j) {
>     return i + j;
> }
>
> void main(string[] args) {
>
>     enforce(args.length > 1, "An argument must be passed in");
>
>     auto arg = args[1].to!int; //to prove it's at runtime
>     auto adderPartial = functorPartial!adder(arg); //runtime 
> value
>     writeln("adder result: ", adderPartial(4));
>
>     //"subtracter"? "subtractor"? who cares
>     auto subtracterPartial = functorPartial!((a, b) => a - 
> b)(arg);
>     writeln("subtracter partial: ", subtracterPartial(4));
>
> }

Unfortunately this doesn't solve the problem in general with 
@nogc. When passing one of these functors to e.g. 
std.algorithm.map, there is no way to avoid the reference to the 
current scope. The challenge is to implement a (correct, see 
https://issues.dlang.org/show_bug.cgi?id=14982) @nogc version of 
this function without rewriting map:

auto foo(int a)
{
     return iota(10).map!(x => x + a);
}

I don't think it can be done without language changes.

I wonder what could be done if we could get inspect and 
manipulate context pointers in code...


More information about the Digitalmars-d mailing list