Jump to page: 1 2
Thread overview
non-lambda overloads for lambda-only things
Apr 16, 2021
Jordan Wilson
Apr 16, 2021
Ali Çehreli
Apr 16, 2021
Berni44
Apr 16, 2021
Jacob Carlborg
Apr 16, 2021
Paul Backus
Apr 16, 2021
tsbockman
Apr 16, 2021
tsbockman
Apr 16, 2021
Jon Degenhardt
April 15, 2021
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
April 16, 2021
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
April 15, 2021
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

April 16, 2021

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.

April 16, 2021
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
April 16, 2021
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
April 16, 2021

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).

April 16, 2021

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:

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
April 16, 2021

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.

April 16, 2021

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

« First   ‹ Prev
1 2