www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - non-lambda overloads for lambda-only things

reply Steven Schveighoffer <schveiguy gmail.com> writes:
Have you ever written something like:

auto str = "hello".map(r => r.toUpper).array;

and been confronted with fun errors like:

Error: template std.algorithm.iteration.map cannot deduce function from 
argument types !()(string, void), candidates are:
/dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/iteration.d(479): 
       map(fun...)
   with fun = ()
   must satisfy the following constraint:
        fun.length >= 1

What the hell is the "fun length"? This is not FUN!!!

In any case, I thought of maybe adding something like this:

void map()(...) {
    static assert(false, "You didn't mean to do this. Make your lambda a 
template parameter");
}

To instead get a better error message:

Error: static assert:  "You didn't mean to do this. Make your lambda a 
template parameter"

  Is this worth adding to Phobos?

-Steve
Apr 15
next sibling parent Jordan Wilson <wilsonjord gmail.com> writes:
On Thursday, 15 April 2021 at 22:02:10 UTC, Steven Schveighoffer 
wrote:
 Have you ever written something like:

 auto str = "hello".map(r => r.toUpper).array;

 and been confronted with fun errors like:

 Error: template std.algorithm.iteration.map cannot deduce 
 function from argument types !()(string, void), candidates are:
 /dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/iteration.d(479):
       map(fun...)
   with fun = ()
   must satisfy the following constraint:
        fun.length >= 1

 What the hell is the "fun length"? This is not FUN!!!

 In any case, I thought of maybe adding something like this:

 void map()(...) {
    static assert(false, "You didn't mean to do this. Make your 
 lambda a template parameter");
 }

 To instead get a better error message:

 Error: static assert:  "You didn't mean to do this. Make your 
 lambda a template parameter"

  Is this worth adding to Phobos?

 -Steve
As a noob, when I see error messages like the first one, I immediately just pick out the line number, and just stare at the offending code until I realise I've missed an "!". I think your proposed error message tells me instantly what went wrong. Thanks, Jordan
Apr 15
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 4/15/21 3:02 PM, Steven Schveighoffer wrote:
 Have you ever written something like:

 auto str = "hello".map(r => r.toUpper).array;
 To instead get a better error message:

 Error: static assert:  "You didn't mean to do this. Make your lambda a
 template parameter"

   Is this worth adding to Phobos?

 -Steve
YES! :) Coincidentally, I have wasted considerable amount of time just now with the following error message: import std.format; import std.algorithm; void main() { auto content = q"EOS 1 first line 2 second line EOS"; foreach (line; content) { int i; string s; line.formattedRead!"%s %s"(i, s); } } The first line of the 78-line error message is this: /usr/include/dmd/phobos/std/format.d(1492): Error: template `std.range.primitives.empty` cannot deduce function from argument types `!()(immutable(char))`, candidates are: but the line in format.d, which my editor was showing me was this: assert(!r.empty, "Required at least one more input"); I admit that my brain was reading the error message on the source code instead of the actual error message and I was scratching my head, trying to understand what "one more input" meant. The programmer's error was something completely different: 'content' is not an iterable thing; it's a string. Argh! So, one solution is to iterate over content.splitter('\n') in the loop. Ali
Apr 15
prev sibling next sibling parent Berni44 <someone somemail.com> writes:
On Thursday, 15 April 2021 at 22:02:10 UTC, Steven Schveighoffer 
wrote:
  Is this worth adding to Phobos?
YES! When I was a beginner, I did not know about templates parameters. I realized soon, that I sometimes need a `!` to make a function, like `std.conv : to` from Phobos work, but was quite often lost, when getting such "fun" error messages. A hint toward a template parameter would have helped a lot.
Apr 15
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2021-04-16 00:02, Steven Schveighoffer wrote:
 Have you ever written something like:
 
 auto str = "hello".map(r => r.toUpper).array;
 
 and been confronted with fun errors like:
 
 Error: template std.algorithm.iteration.map cannot deduce function from 
 argument types !()(string, void), candidates are:
 /dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/iteration.d(479): 
       map(fun...)
   with fun = ()
   must satisfy the following constraint:
        fun.length >= 1
Yes, definitely. I doesn't help when there are longer chains and they're nested. It usually results in even more cryptic error messages. BTW, what's stopping us from supporting the above syntax? Why do we still pass the lambda as a template argument? In that past, before proper support for lambdas, there was a use case to pass a string literal, which must be passed as a template argument. If DMD cannot inline the lambda, who cares, use LDC instead. -- /Jacob Carlborg
Apr 16
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/16/21 7:23 AM, Jacob Carlborg wrote:
 On 2021-04-16 00:02, Steven Schveighoffer wrote:
 Have you ever written something like:

 auto str = "hello".map(r => r.toUpper).array;

 and been confronted with fun errors like:

 Error: template std.algorithm.iteration.map cannot deduce function 
 from argument types !()(string, void), candidates are:
 /dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/iteration.d(479):       
 map(fun...)
   with fun = ()
   must satisfy the following constraint:
        fun.length >= 1
Yes, definitely. I doesn't help when there are longer chains and they're nested. It usually results in even more cryptic error messages. BTW, what's stopping us from supporting the above syntax? Why do we still pass the lambda as a template argument? In that past, before proper support for lambdas, there was a use case to pass a string literal, which must be passed as a template argument. If DMD cannot inline the lambda, who cares, use LDC instead.
It could possibly work, if you specify the type. But I haven't thought about the ramifications. I want to avoid creating closures, and as you say, inlining might be affected. -Steve
Apr 16
parent Paul Backus <snarwin gmail.com> writes:
On Friday, 16 April 2021 at 14:02:24 UTC, Steven Schveighoffer 
wrote:
 On 4/16/21 7:23 AM, Jacob Carlborg wrote:
 BTW, what's stopping us from supporting the above syntax? Why 
 do we still pass the lambda as a template argument? In that 
 past, before proper support for lambdas, there was a use case 
 to pass a string literal, which must be passed as a template 
 argument. If DMD cannot inline the lambda, who cares, use LDC 
 instead.
 
It could possibly work, if you specify the type. But I haven't thought about the ramifications. I want to avoid creating closures, and as you say, inlining might be affected.
You'd have to use `&` if you wanted to pass a named function rather than a lambda (e.g., `map(&fun)`), and you wouldn't be able to pass template functions (e.g., `map(&to!string)` would be a compile-time error).
Apr 16
prev sibling parent tsbockman <thomas.bockman gmail.com> writes:
On Friday, 16 April 2021 at 11:23:07 UTC, Jacob Carlborg wrote:
 BTW, what's stopping us from supporting the above syntax? Why 
 do we still pass the lambda as a template argument? In that 
 past, before proper support for lambdas, there was a use case 
 to pass a string literal, which must be passed as a template 
 argument. If DMD cannot inline the lambda, who cares, use LDC 
 instead.
When a function or delegate is passed as a runtime argument, it cannot be inlined unless the function it is being passed to is also inlined. But, when one is passed as a template argument it can be inlined even if the function receiving it cannot or should not be inlined. This limitation is fundamental to the language, not a weakness of any particular implementation: ```D module app; auto fRT(int function(int) g, int x) { pragma(inline, false); // For demonstration purposes. return g(x * 7); } auto fCT(alias g)(int x) if(is(typeof(g) : int function(int))) { pragma(inline, false); // For demonstration purposes. return g(x * 7); } void main() { import std.stdio : writeln; /* g cannot be inlined in fRT, because g may be different every time f is called: */ writeln(fRT((int y) { return y + 3; }, 4)); writeln(fRT((int y) { return y - 2; }, 2)); /* But, g can be inlined in some possible instantiations of fCT, because each instantiation is a separate function at runtime: */ writeln(fCT!((int y) { return y + 3; })(4)); writeln(fCT!((int y) { return y - 2; })(2)); } ``` Part of the resulting assembly code with LDC `-m64 -mcpu=haswell -O3 -release`: ``` int app.fRT(int function(int)*, int): lea eax, [8*rdi] sub eax, edi mov edi, eax jmp rsi pure nothrow nogc safe int app.fCT!(app.main().__lambda3(int)).fCT(int): lea eax, [8*rdi] sub eax, edi add eax, 3 ret pure nothrow nogc safe int app.fCT!(app.main().__lambda4(int)).fCT(int): lea eax, [8*rdi] sub eax, edi add eax, -2 ret ```
Apr 16
prev sibling next sibling parent reply tsbockman <thomas.bockman gmail.com> writes:
On Thursday, 15 April 2021 at 22:02:10 UTC, Steven Schveighoffer 
wrote:
 To instead get a better error message:

 Error: static assert:  "You didn't mean to do this. Make your 
 lambda a template parameter"

  Is this worth adding to Phobos?
Rather than adding a bunch of boiler plate to every generic library to get better error messages, why not make the compiler do it automatically? Whenever the compiler generates an error message due to failure of function overload resolution and only runtime arguments were supplied (syntactically), the compiler could first test whether overload resolution would have succeeded if a `!` had been included to mark them as template arguments, instead. It should still be an error either way, but in the latter case a "did you mean ...?" suggested resolution could be included in the message. This generally shouldn't slow down compilation meaningfully, since it only triggers when the compilation is going to fail and skip code generation, etc. anyway.
Apr 16
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/16/21 4:36 PM, tsbockman wrote:
 On Thursday, 15 April 2021 at 22:02:10 UTC, Steven Schveighoffer wrote:
 To instead get a better error message:

 Error: static assert:  "You didn't mean to do this. Make your lambda a 
 template parameter"

  Is this worth adding to Phobos?
Rather than adding a bunch of boiler plate to every generic library to get better error messages, why not make the compiler do it automatically? Whenever the compiler generates an error message due to failure of function overload resolution and only runtime arguments were supplied (syntactically), the compiler could first test whether overload resolution would have succeeded if a `!` had been included to mark them as template arguments, instead. It should still be an error either way, but in the latter case a "did you mean ...?" suggested resolution could be included in the message. This generally shouldn't slow down compilation meaningfully, since it only triggers when the compilation is going to fail and skip code generation, etc. anyway.
Yes please! Can someone do this? Would be way better than the phobos hack I am thinking of. -Steve
Apr 16
prev sibling parent Jon Degenhardt <jond noreply.com> writes:
On Thursday, 15 April 2021 at 22:02:10 UTC, Steven Schveighoffer 
wrote:
 Have you ever written something like:

 auto str = "hello".map(r => r.toUpper).array;

 and been confronted with fun errors like:

 Error: template std.algorithm.iteration.map cannot deduce 
 function from argument types !()(string, void), candidates are:
 /dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/iteration.d(479):
       map(fun...)
   with fun = ()
   must satisfy the following constraint:
        fun.length >= 1

 What the hell is the "fun length"? This is not FUN!!!

 In any case, I thought of maybe adding something like this:

 void map()(...) {
    static assert(false, "You didn't mean to do this. Make your 
 lambda a template parameter");
 }

 To instead get a better error message:

 Error: static assert:  "You didn't mean to do this. Make your 
 lambda a template parameter"

  Is this worth adding to Phobos?

 -Steve
Yes!! As others have said, this hit me when I first started learning D. I just wasn't expecting to pass the lambda as a template parameter. An error message that more quickly pointed to what needed to be done would have removed a start-up barrier. --Jon
Apr 16