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