Thread overview
Cannot use local lambda as parameter to non-global template
Jan 15, 2018
Nordlöw
Jan 15, 2018
Simen Kjærås
Jan 15, 2018
Jacob Carlborg
Jan 15, 2018
Meta
Jan 15, 2018
Ali Çehreli
Jan 15, 2018
Nordlöw
January 15, 2018
Why do I get errors like

template instance remove!((_) => _ == 1) cannot use local '__lambda5' as parameter to non-global template remove()(in K key)

for

    x.remove!(_ => _ == 1);

but not for

    x.remove!"a == 11";

?

How are these two lambdas different?

The function `remove` is a templated member of a struct instance `x`.
January 15, 2018
On Monday, 15 January 2018 at 13:55:57 UTC, Nordlöw wrote:
> Why do I get errors like
>
> template instance remove!((_) => _ == 1) cannot use local '__lambda5' as parameter to non-global template remove()(in K key)
>
> for
>
>     x.remove!(_ => _ == 1);
>
> but not for
>
>     x.remove!"a == 11";
>
> ?
>
> How are these two lambdas different?
>
> The function `remove` is a templated member of a struct instance `x`.

It's a bit involved, but boils down to this: lambdas in D can only have one context pointer. A simple example:

class Foo {
    int n = 2;
    int run(alias fun)(int m) { return fun(n, m); }
}

unittest {
    int a = 3;
    int fn(int n, int m) { return a + n + m; }
    assert((new Foo).run!fn(4) == 9);
}

fn obviously requires a pointer to the context in the unittest block, since it uses 'a' in that context. Somewhat less obviously, it also needs a pointer to the Foo instance in which it is run. Since 2 > 1, this fails. There's a bug reported for it: https://issues.dlang.org/show_bug.cgi?id=5710, and even a $200 bounty for fixing it: https://www.bountysource.com/issues/1375082

The bug report is almost seven years old, so it seems this is a hard bug to squash.

Luckily, there are some workarounds. One you've already found. Others include moving templates to scopes where less context is needed (e.g. moving a function template that is associated with a class, but doesn't need to be part of it, outside the class), and marking your lambdas 'static'. A static lambda does not have a context pointer to the scope in which it is declared, and so cannot use variables declared in it. For instance, fn above uses 'a' in the same scope, and so cannot be marked static.

--
  Simen
January 15, 2018
On 2018-01-15 14:55, Nordlöw wrote:
> Why do I get errors like
> 
> template instance remove!((_) => _ == 1) cannot use local '__lambda5' as parameter to non-global template remove()(in K key)
> 
> for
> 
>      x.remove!(_ => _ == 1);
> 
> but not for
> 
>      x.remove!"a == 11";
> 
> ?
> 
> How are these two lambdas different?
> 
> The function `remove` is a templated member of a struct instance `x`.

The difference is that the string version would not capture the surrounding context while the lambda version does. It's something related to that the lambda needs to capture both the this object, since "remove" is member of a struct, and capturing the local context of where "remove" is called. A lambda/delegate can only have one context pointer. There's an issue already reported for this.

-- 
/Jacob Carlborg
January 15, 2018
On Monday, 15 January 2018 at 13:55:57 UTC, Nordlöw wrote:
> Why do I get errors like
>
> template instance remove!((_) => _ == 1) cannot use local '__lambda5' as parameter to non-global template remove()(in K key)
>
> for
>
>     x.remove!(_ => _ == 1);
>
> but not for
>
>     x.remove!"a == 11";
>
> ?
>
> How are these two lambdas different?
>
> The function `remove` is a templated member of a struct instance `x`.

Can you give a bit more context? The following code does not cause a compile error:

import std.stdio;
import std.algorithm;

void main()
{
    auto arr = [1, 2, 3];
    arr.remove!(_ => _ == 1);
    writeln(arr);
}

Or was that code meant as an example?
January 15, 2018
On 01/15/2018 07:27 AM, Meta wrote:
> On Monday, 15 January 2018 at 13:55:57 UTC, Nordlöw wrote:

>> The function `remove` is a templated member of a struct instance `x`.
> 
> Can you give a bit more context? The following code does not cause a compile error:
> 
> import std.stdio;
> import std.algorithm;
> 
> void main()
> {
>      auto arr = [1, 2, 3];
>      arr.remove!(_ => _ == 1);
>      writeln(arr);
> }
> 
> Or was that code meant as an example?

import std.algorithm;

struct S {
    int[] elements;

    void remove(alias func)() {
        // ...
    }

    void remove(string s)() {
        foreach (a; elements) {
            const cond = mixin(s);
            if (cond) {
                // do remove
            }
        }
    }
}

void main()
{
    auto s = S();
    s.remove!"a == 11"();

    // Fails compilation:
    // s.remove!(_ => _ == 1);
}

Ali
January 15, 2018
On Monday, 15 January 2018 at 15:27:23 UTC, Meta wrote:
> void main()
> {
>     auto arr = [1, 2, 3];
>     arr.remove!(_ => _ == 1);
>     writeln(arr);
> }
>
> Or was that code meant as an example?

The problem occurs when the templated function is a member of the struct `arr`. I've moved the algorithm into a free function here

https://github.com/nordlow/phobos-next/blob/master/src/hashmap_or_hashset.d#L1190

instead. I've changed the name to `removeAllMatching` for now.