On Wednesday, 2 January 2013 at 21:12:33 UTC, Maxim Fomin wrote:
On Wednesday, 2 January 2013 at 21:00:10 UTC, Michael wrote:
R With(I, R)(I o, R function (I) fun)
{
static if(isAssignable!(I, typeof(null)))
return o is null ? null : fun(o);
else
return fun(o);
}
...
in main function
----------------
foreach(p; persons)
p.With((Person x) => x.address); // works
but
----------------
foreach(p; persons)
p.With(x => x.address); // error
nullcheck.d(89): Error: template maybe.With does not match any
function template
declaration. Candidates are:
maybe.d(20): maybe.With(I, R)(I o, R function(I) fun)
nullcheck.d(89): Error: template maybe.With(I, R)(I o, R
function(I) fun) cannot
deduce template function from argument types !()(Person,void)
Why?
The first one is a lambda function, the second one is a lambda
template. Templates have type void.
Actually the 'void' is just a diagnostics bug. (lambda templates
only exist in template parameter lists.) The reason the matching
fails is that IFTI is not smart enough to devise a type for the
parameter 'x', and therefore no type for 'R' is obtained, what
makes the matching fail. Currently other parameters are not taken
into account during IFTI matching. I believe that the reason is
that otherwise the compiler needs to be clever about the order in
which it analyzes the parameters. I consider this one of the more
annoying limitations.
A workaround that should work in this case is to use a template
parameter:
auto With(alias fun, I)(I o) // maybe add a template constraint
here
{
static if(isAssignable!(I, typeof(null)))
return o is null ? null : fun(o);
else
return fun(o);
}
---
foreach(p; persons)
p.With!(x => x.address);
This creates a template from the lambda, and instantiates it once
inside 'With'. This way the return type does not have to be part
of the template parameters.