Re: a lambda with arguments has type void?

2016-06-09 Thread Artur Skawina via Digitalmars-d-learn
On 06/09/16 07:20, cy via Digitalmars-d-learn wrote:
> 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));

No. `a=>a+1` is a /template/ and is passed as-is to `foo2`. Hence
`c` is a template and it's only instantiated inside that function.
This would compile too:

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


>   // 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;
> }

It can't. It's a template. Templates are not values and can not be
used as runtime function arguments.

The only magic that the compiler does is that it lets you call
callable templates directly - it automatically instantiates them
(using the types of the arguments used for that call). 

artur


Re: a lambda with arguments has type void?

2016-06-08 Thread ketmar via Digitalmars-d-learn

On Thursday, 9 June 2016 at 05:20:46 UTC, cy wrote:

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.


yes, you can find alot of situations where it is theoretically 
possible to infer types (sometimes by doing several passes to 
"refine" the info), but dmd currently doesn't do that. partially 
because it's a good amount of work, partially because it will 
slow down the compiler considerably in some cases.


Re: a lambda with arguments has type void?

2016-06-08 Thread cy via Digitalmars-d-learn

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 RGH


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.


Re: a lambda with arguments has type void?

2016-06-07 Thread ag0aep6g via Digitalmars-d-learn

On 06/08/2016 12:02 AM, cy wrote:

import std.stdio;

void foo(Callable)(Callable bar) {
   bar();
}

void foo2(Callable)(Callable bar, int baz) {
   bar(baz);
}

void main() {
   foo({
   writeln("okay");
 });
   foo2((bar) {
   writeln("got",bar);
 },42);
   foo2((bar) => writeln("yay",bar), 42);
}



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. That they're called with an int 
in foo2's body is not taken into consideration at that point.


You can:
* make it `int bar` in the literals, or
* explicitly take a `void delegate(int)` or `void function(int)` in foo2, or
* take the callback as a "template alias parameter":

import std.stdio;
void foo2(alias bar)(int baz) { bar(baz); }
void main() {
  foo2!((bar) { writeln("got", bar); })(42);
  foo2!(bar => writeln("yay", bar))(42);
}



a lambda with arguments has type void?

2016-06-07 Thread cy via Digitalmars-d-learn
This program errors out, when I try to pass lambdas that take 
arguments. How am I getting the syntax wrong here? Is there some 
reason you can't do this?


import std.stdio;

void foo(Callable)(Callable bar) {
  bar();
}

void foo2(Callable)(Callable bar, int baz) {
  bar(baz);
}

void main() {
  foo({
  writeln("okay");
});
  foo2((bar) {
  writeln("got",bar);
},42);
  foo2((bar) => writeln("yay",bar), 42);
}