Idea for avoiding GC for lambdas

12345swordy alexanderheistermann at gmail.com
Wed Jun 23 00:38:48 UTC 2021


On Monday, 21 June 2021 at 20:01:27 UTC, Steven Schveighoffer 
wrote:
> I have some code in my codebase that does stuff like this:
>
> ```d
> struct S
> {
>    int id;
>    int field1;
> }
>
> auto foo(S[] arr, int someval) // @nogc not allowed :(
> {
>    // remove all the elements of arr that have someval for 
> field1
>    import std.algorithm;
>    return arr.filter!(v => v.field1 == someval);
> }
> ```
>
> While this works, and is nice, it's using a lambda with local 
> references to filter data. This means a GC closure allocation 
> is involved.
>
> However, the value that is referenced (someval) is the only 
> thing needed, and it seems a huge waste to allocate a stack 
> frame *just* for that.
>
> Let's look at another way to do this. `filter` does not have an 
> overload which accepts a *value* to compare to, but `find` 
> does. So let's build a new `filter` that uses it:
>
> ```d
> auto nogcfilter(alias pred, T, V)(T[] rng, V val)
> {
>     import std.range;
>     import std.algorithm : find;
>     static struct Filter {
>         T[] src;
>         V val;
>         auto front() { return src.front; }
>         void popFront() {src.popFront; src = 
> src.find!pred(val); }
>         auto empty() { return src.empty; }
>     }
>     return Filter(rng, val);
> }
>
> auto foo(S[] arr, int someval) @nogc // :)
> {
>    // note: the explicit lambda types are needed for some reason
>    return arr.nogcfilter!((S v, int a) => v.field1 == 
> a)(someval);
> }
> ```
>
> Now we get filter capability, but without the GC.
>
> This works great because the context items used do NOT require 
> any mutability. It also works great because the filter function 
> is building a custom type ANYWAY.
>
> Now, we could add this kind of functionality to `filter`, but 
> being the lazy programmer that I am, what if the compiler could 
> do this for me? In most cases, I don't want to sleuth out what 
> context is needed (which might change as the code evolves). To 
> force the user to both invent the context type (here, it's just 
> an `int`, but it may require other values) and explicitly pass 
> it is a bit verbose (I already don't like having to pass the 
> `int`, and declaring the parameter).
>
> What if, a function that accepts a lambda, could also accept a 
> *context* which the compiler made up and passed in? Here's how 
> it would possibly look (with a new magic type `__context`):
>
> ```d
> auto nogcfilter(alias pred, T, __context...)(T[] rng, __context 
> ctxt)
> {
>     import std.range;
>     import std.algorithm : find;
>     static struct Filter {
>         T[] src;
>         __context val;
>         auto front() { return src.front; }
>         void popFront() {src.popFront; src = 
> src.find!pred(val); }
>         auto empty() { return src.empty; }
>     }
>     return Filter(rng, ctxt);
> }
>
> auto foo(S[] arr, int someval) @nogc
> {
>    return arr.nogcfilter!((v) => v.field1 == someval);
> }
> ```
>
> The compiler would see there's a lambda involved, automatically 
> pass the context parameter `someval` into the function itself, 
> and everything magically works. It would only be used when a 
> closure would otherwise be allocated, and the function being 
> called declares the context parameter.
>
> Disclaimer: I am not a language developer.
>
> Does this make sense? Is there anything feasible that might be 
> similar? Would it be more feasible with different syntax?
>
> I realize there are implications if the context parameter is 
> mutable, so perhaps we would have to require only const 
> contexts (though the compiler might be able to figure out that 
> something isn't modified anywhere, and allow it to go in).
>
> -Steve

Why can't it be implemented by using templates?

- Alex


More information about the Digitalmars-d mailing list