a lambda with arguments has type void?

cy via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Jun 8 22:20:46 PDT 2016


On Tuesday, 7 June 2016 at 22:17:03 UTC, ag0aep6g wrote:
> You don't specify the types of the parameters of the function 
> literals, so you effectively have templates there. As such the 
> literals have no types, and can't be passed as arguments.

Yeah, I see that now. The compiler does have all the necessary 
information to infer the argument types in both templates, 
though. There's no reason that it /couldn't/ infer the type of 
the argument. Like this is why it doesn't really make sense:

import std.stdio;

auto foo(Callable)(Callable c) {
   return c(42);
}

auto foo2(alias c)() {
   return c(42);
}

void main() {
   // this works, when you know it's an int delegate(int) 
beforehand...
   writeln(foo!(int delegate(int))((arg) => arg + 1));
   // and this can infer that your argument is an int delegate(int)
   writeln(foo2!((arg) => arg + 1));
   // so why doesn't this work, if the compiler can infer that the
   // argument is an int delegate(int)?
   static assert(!__traits(compiles,
	 writeln(foo((arg) => arg + 1))));
}

My guess the reason this doesn't work is: nobody worked on it yet.

> You can:
> * make it `int bar` in the literals, or

Sure, but it's a bit of a pain to have to export SomeArgumentType 
just so my callbacks can go ((SomeArgumentType rows) => ...). I 
don't like anything that makes you have to go through even more 
trouble to specify your imported symbols explicitly.

> * explicitly take a `void delegate(int)` or `void 
> function(int)` in foo2,

The problem with that is you don't always know if it's going to 
be a void delegate(int) or a void function(int). You can use 
"toDelegate" to force the issue, but that's more boilerplate 
required for the caller, and really that's the purpose of 
templates, to adjust your code to fit whether a function pointer 
or a delegate pointer or whatever is being passed.

Or you could overload the function, copying and pasting all the 
code, one for "void delegate(int)" and one for "void 
function(int)". Which is exactly what templates are SUPPOSED to 
prevent, but in this case they just... don't.

> * take the callback as a "template alias parameter":

Sure, that works great, except when the source is a member 
function. Then it doesn't work at all. But since D has the trick, 
where you can take a non-member-function and make it act like 
one, it's workable.

Like this is the problem:

import std.stdio;

version(logical) {
   struct foo {
	int arg;
	void bar(alias c)() {
	  c("foobar",arg);
	}
   }
} else {

   struct foo {
	int arg;
   }

   void bar(alias c, foo)(ref foo f) {
	c("foobar",f.arg);
   }

   void bar(alias c, foo)(foo* f) {
	return bar!(c,foo)(*f);
   }
}

void main() {
   import std.stdio;
   foo f = foo(42);

   f.bar!((message,arg) => writeln(message,arg));
}

Not to mention if "foo/bar" is defined in a support module, you 
now have to separately import "bar" to get "struct foo" to work 
right, since it won't import along with "foo", which makes it 
more difficult to use explicitly named imports, and I try never 
to do that. Anything that makes it more difficult to explicitly 
name imports encourages people to write BAD CODE THAT NOBODY CAN 
READ BECAUSE WHERE DID THAT SYMBOL COME FROM AAAARGH

ahem

It's probably the lesser of three evils, using 
non-member-function templates, to take a template as a callback. 
I can definitely see a use for being able to specialize templates 
using inferred types, rather than ones explicitly passed to the 
!() list.


More information about the Digitalmars-d-learn mailing list