typeof function literals which define the types of its parameters but do not give their parameters names

Johannes Loher johannes.loher at fg4f.de
Thu Dec 27 08:53:30 UTC 2018


On Thursday, 27 December 2018 at 04:27:03 UTC, Steven 
Schveighoffer wrote:
> On 12/26/18 10:52 PM, Johannes Loher wrote:
>> Hey all,
>> 
>> I am a bit confused about the inferred types of function 
>> literals which do not name their parameters (something like 
>> `(int) {}`). The confusion arises from the fact that the 
>> inferred type sometimes is `void` (might be the case, because 
>> the function literal is inferred to be a template) and 
>> sometimes it is something like `void function(bool _param_0) 
>> pure nothrow @nogc @safe`. What is really weir is that seems 
>> to depend on the type of the parameter. Here is small example 
>> showing what happens for different parameter types: 
>> https://run.dlang.io/is/xSMZZu
>> 
>> Also note that this only happens for function literals. For 
>> regular functions and member functions, `void` is never 
>> inferred, but only types like `pure nothrow @nogc @safe 
>> void(MyClass _param_0)`.
>> 
>> Has anybody any idea what is going on here? Is this a bug?
>
> This is pretty funny actually.
>
> Note the difference between the ones that print something other 
> than "void" is that the parameters are all keywords!
>
> The ones that do print "void" have a normal symbol name as 
> their parameter. This could be a parameter name or a parameter 
> type, and the compiler is choosing the latter.
>
> In other words, you are expecting:
>
> alias f = (string) {}
>
> to evaluate to something like:
>
> void f(string) {}
>
> but in fact, it evaluates to:
>
> void f(T)(T string) {}
>
> because string is not a keyword, it's an alias. So you are free 
> to redefine it inside your lambda (in this case, as a template 
> parameter *name*).
>
> As proof, you can try calling it like this:
>
> f(1)
>
> and it works.
>
> To fix, just give all your lambda parameters names.
>
> -Steve

Thanks for the insight, this is indeed actually very funny. You 
can even force the compiler to interpret the parameters as types 
in several ways:

```
alias j = (typeof(string.init)) { };

alias identity(T) = T;
alias k = (identity!size_t) { };

```

or if the type is defined at module level, this works, too:

```
alias l = (.MyStruct) { };
```

There are probably a lot more. You just need to use some 
expression which evaluates to the actual type but cannot be 
interpreted as an identifier (".", "!", "(" and ")" are not 
allowed inside identifiers, which is how I suppose the above 
examples work).

However, this still leaves the question whether this behavior is 
intentional or not. It is at least very surprising and 
inconsistent in my opinion. For example consider some generic 
code using `std.traits.isSomeFunction` (which is actually how I 
found out about this):

```
static assert(isSomeFunction!((long) {});
static assert(!isSomeFunction!((size_t) {});
```

If this behavior is indeed intentional, it should at least be 
covered in the spec.



More information about the Digitalmars-d-learn mailing list