1
votes

In the function below, if I change a to kv:

void main()
{
    import std.algorithm.searching : minElement;
    import std.stdio : writeln;
    import std.array: byPair;

    long[string] aa = [
        "foo": 5,
        "bar": 10,
        "baz": 2000
    ];
    writeln(aa.byPair().minElement!"a.value"().value); 
}

compiler throws the following error message:

/dlang/dmd/linux/bin64/../../src/phobos/std/functional.d-mixin-215(215): Error: undefined identifier kv /dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/searching.d(1351): Error: template instance std.functional.binaryFun!("kv.value", "a", "b").binaryFun!(Tuple!(string, "key", long, "value"), Tuple!(string, "key", long, "value")) error instantiating /dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/searching.d(1314): instantiated from here: extremum!(__lambda2, "kv.value", MapResult!(__lambda2, Result), Tuple!(string, "key", long, "value")) /dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/searching.d(1398): instantiated from here: extremum!((a) => a, "kv.value", MapResult!(__lambda2, Result)) /dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/searching.d(3550): instantiated from here: extremum!("kv.value", MapResult!(__lambda2, Result)) onlineapp.d(12): instantiated from here: minElement!("kv.value", MapResult!(__lambda2, Result))

But compiles fine with just "a.value" argument. What does this a mean?

2
instead of the quote "a.whatever" you can use a => a.whatever without quotes now in new D versions (newer than like 3 years). the "" thing is kinda legacy now and less reliable) - Adam D. Ruppe

2 Answers

4
votes

minElement uses unaryFun to turn the passed string into a function. However, to do this it uses string mixins. The downside to this is the generated function doesn't have access to the context in which the string is created, and thus can't access the variables there.

As unaryFun's documentation says, the parameter name in the string must be a. This explains why kv fails.

Of course, as Adam D. Ruppe says, you should instead use the newer lambda syntax kv => kv.value - this allows you to us whatever parameter names you want, and allows access to the context, letting you do things like minElement!(kv => kv.value + aa["foo"]), which is simply impossible with the string functions.

Lastly, one of the possibly best reasons not to use the string functions is, as you've noticed, the error messages. Since the conversion from string to functions happens deep inside a stack of templates, you get a list of unrelated locations when the actual error is in your own code, while a lambda would show you exactly what's wrong in an easy-to-grok error message.

2
votes

String parameters as functions appear as examples everywhere in std documentation, but when and how they work isn't documented very well. As you have noticed, std templates that take a function alias parameter can receive a string instead of an actual function.

This string is then converted to a "real" function using unaryFun or binaryFun which use mixin or some other magic. They name the parameters a and b, which you can use.

As Adam D. Ruppe has noted, you can also pass "normal" functions/delegates like minElement!(a => a.value)() or minElement!((a){ return a.value; }), of course parameter names are up to you then.