Re: About the callable native lambda

2018-02-01 Thread Jesper Steen Møller
Hi again 

> On 1 Feb 2018, at 13.48, Jochen Theodorou  wrote:
> 
> [...]
>> For static compilation, r1() and r2() should work IMHO, and r3() should be 
>> rejected. This shouldn't surprise anyone, I think.
> 
> you mean because the m has a return value and r3() is void? That is currently 
> passing compilation and the result will be null. This covers with the dynamic 
> version.
> 

No, I mean because r3 (statically) is a java.lang.Object, and Object has no 
call() method and is not a SAM:

> you surely did mean:
> 
>>> def n(Runnable q1, Predicate q2, Q q2, q3) {
>>> q1()
>>> q2()
>>> q3() >>  q4()
>>> }
>>> m(new Q(), new Q(), new Q(), new Q())
> 
> I expect this to work then too ;)


Yeah, the mail wasn't complete. I've fixed the example, re-sent the mail, and 
suggested (IMHO) non-surprising semantics.

-Jesper



Re: About the callable native lambda

2018-02-01 Thread Jochen Theodorou



Am 01.02.2018 um 12:50 schrieb Jesper Steen Møller:
[...]

class R implements Runnable { void run(){}}

def m(Runnable r1, R r2, r3) {
 r1()
 r2()
 r3()
}

m(new R(), new R(), new R())

Currently, in the 'native-lambda' branch, r1() succeeds, whereas the latter two 
fail to run/compile (depending on dynamic/static compilation), as 'avafanasiev' 
commented on. I do find that confusing: Dynamically, my opinion is that the 
three should work the same.

For static compilation, r1() and r2() should work IMHO, and r3() should be 
rejected. This shouldn't surprise anyone, I think.


you mean because the m has a return value and r3() is void? That is 
currently passing compilation and the result will be null. This covers 
with the dynamic version.



Also, surprise-wise,

class Q implements Runnable, Predicate { void run(){}; boolean 
test(String s) { s } }

def n(Runnable q1, Predicate q2, Q q2, q3) {
 r1()
 r2()
 r3()
}

m(new R(), new R(), new R())



you surely did mean:


def n(Runnable q1, Predicate q2, Q q2, q3) {
 q1()
 q2()
 q3() >>  q4()
}

m(new Q(), new Q(), new Q(), new Q())


I expect this to work then too ;)


bye Jochen


Re: About the callable native lambda

2018-02-01 Thread Jesper Steen Møller
(Sorry, first version went out too soon, thanks to the silly, silly Touch Bar)

> On 31 Jan 2018, at 23.01, MG  wrote:
> 
> Hi Jesper,
> 
> seen from a Groovy user perspective your proposal seems to make sense to me.
> (I would at the same time hope you do not dent Daniel Sun's enthusiasm too 
> much, because as far as I can tell he is currently doing alot of the heavy 
> lifting in this project :-) )
> 
Yes, he is - and the lambda work has come a long way - I'm testing some 
scenarios, so i hope to lift a little, too!

> How do you think what you propose fares with regards to "the principle of 
> least surprise" ? Are there any cases where this could lead to hard to track 
> bugs / unexpected behavior ? From the top of my hat, that would be my biggest 
> concern...

I see static and dynamic as different concerns: For dynamic invocation, there 
could be some surprises, like example posted by 'avafanasiev' on GitHub:

class R implements Runnable { void run(){}}

def m(Runnable r1, R r2, r3) {
   r1()
   r2()
   r3()
}

m(new R(), new R(), new R())

Currently, in the 'native-lambda' branch, r1() succeeds, whereas the latter two 
fail to run/compile (depending on dynamic/static compilation), as 'avafanasiev' 
commented on. I do find that confusing: Dynamically, my opinion is that the 
three should work the same.

For static compilation, r1() and r2() should work IMHO, and r3() should be 
rejected. This shouldn't surprise anyone, I think.

Also, surprise-wise, consider:

class Q implements Runnable, Supplier { void run(){}; String get() { 
"Q" } }

def n(Runnable q1, Supplier  q2, Q q2, q3) {
   q1()
   q2()
   q3()
   q4()
}

n(new Q(), new Q(), new Q(), new Q())

I'm thinking that all four should FAIL dynamically, but statically, q1 and q2 
should work, using the appropriate interface.

> "...only as a fallback if obj.call  doesn't exist" seems like the safer 
> choice in this regard. Default behavior could also be made overridable by a 
> class annotation (then it would become the programmer's responsibility, to 
> make sure least surprise is not violated).
> Without that the question to me is: Would choosing "fallback if obj.call  
> doesn't exist" weaken the elegance of the whole concept too much ?

I don't think it would weaken the elegance.
We hardly need annotations - we could simply use the presence of GroovyCallable 
to find out which objects would prefer to be call()'ed directly.

-Jesper




Re: About the callable native lambda

2018-01-31 Thread Daniel Sun
Hi Jesper,

Given `f` is of SAM type, `f(params...)` is actually
`f.call(params...)`, which I transform to `f.(params...)`
eventually[1].

The reason why I do not choose `f.(params...)` at the
beginning is that I'm not sure whether we can get enough type information of
`f`, in addition, as you said, the better solution is a breaking change. It
needs discussion in mailing list, which will take a long time to finish...

Cheers,
Daniel.Sun

[1]
https://github.com/apache/groovy/blob/c24c0b7e6a67dcdf277207d4261cfa6f2b55031f/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java#L515



--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html


Re: About the callable native lambda

2018-01-31 Thread MG

Hi Jesper,

seen from a Groovy user perspective your proposal seems to make sense to me.
(I would at the same time hope you do not dent Daniel Sun's enthusiasm 
too much, because as far as I can tell he is currently doing alot of the 
heavy lifting in this project :-) )


How do you think what you propose fares with regards to "the principle 
of least surprise" ? Are there any cases where this could lead to hard 
to track bugs / unexpected behavior ? From the top of my hat, that would 
be my biggest concern...


"...only as a fallback if obj.call  doesn't exist" seems like the safer 
choice in this regard. Default behavior could also be made overridable 
by a class annotation (then it would become the programmer's 
responsibility, to make sure least surprise is not violated).
Without that the question to me is: Would choosing "fallback if 
obj.call  doesn't exist" weaken the elegance of the whole concept too much ?


mg


On 31.01.2018 10:00, Jesper Steen Møller wrote:

Hi list

FYI: This turned into a discussion of  the feature itself, on the GitHub commit thread. 
Basically, I'm proposing changing what "obj(params...)" means:
  - Non-SAM types: obj(params...) becomes obj.call(params...)
  - SAM types: obj(params...) becomes obj.(params...) - perhaps 
only as a fallback if obj.call doesn't exist.

This should be completely independent of how the lambda object itself was 
created.

I realize this is a potentially breaking change, but isn't it also a nice one?
Thoughts?

-Jesper


On 31 Jan 2018, at 03.16, Daniel Sun  wrote:

Hi Jesper,

 I think your suggestion is very nice and I've completed callable native
lambda according to option 2 :-)

 Here is the related commit:
https://github.com/apache/groovy/commit/c24c0b7e6a67dcdf277207d4261cfa6f2b55031f

Cheers,
Daniel.Sun



--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html






Re: About the callable native lambda

2018-01-31 Thread Daniel Sun
Hi Rémi,

As we can see, operandStack.pop() is commented, so the code will not
take effect ;)

Jochen told me some operands left in the stack, I should have pop them
by myself, currently framework pops them for me automatically... so I tried
operandStack.pop(), but I got AIOOBE because stack is empty...

Cheers,
Daniel.Sun



--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html


Re: About the callable native lambda

2018-01-31 Thread Remi Forax


- Mail original -
> De: "Daniel Sun" 
> À: "dev" 
> Envoyé: Mardi 30 Janvier 2018 01:14:25
> Objet: About the callable native lambda

> Hi all,
> 

[...]

>   In addition, have you any idea about the issue[1]? If you do not know
> off the top of your head, I'll have to investigate when I have some spare
> time. Any help is appreciated!

you have a call to operandStack.pop() at the end of the method writeLambda, is 
it what you want ?

> 
> Cheers,
> Daniel.Sun
> 

Rémi

> [1]
> https://github.com/apache/groovy/blob/native-lambda/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java#L136
> 
> 
> 
> --
> Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html


Re: About the callable native lambda

2018-01-31 Thread Daniel Sun
Hi Rémi,

 Thanks for your detailed explanation!

> so (2) seems to be a better option. 
 Yeah, I've chosen the option 2 :-)

  Here is the PR: https://github.com/apache/groovy/pull/654

  Welcome to review it ;-)

Cheers,
Daniel.Sun



--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html


Re: About the callable native lambda

2018-01-31 Thread Remi Forax
You should try to reuse the LambdaMetaFactory instead of generating your own 
proxy,
- generating a proxy means you have a way to define a class in module that do 
not own, so you have to use lookup.defineClass (or worst unsafe.defineClass), 
so you need to generate an invokedynamic
- you can generate the proxy at compile time but in that case, you loose the 
fact that lambdas reduce of disk footprint.
- JDK lambdas use a lightweight classloader so their code is unloaded if the 
lambda is GCed, something which is hard to emulate with your own proxy,
- JDK lambdas consider captured values as really final fields trusted by the 
VM, again something hard to emulate.

so (2) seems to be a better option.

cheers,
Rémi

- Mail original -
> De: "Daniel Sun" 
> À: "dev" 
> Envoyé: Mardi 30 Janvier 2018 01:14:25
> Objet: About the callable native lambda

> Hi all,
> 
>  I'm trying to implement the callable native lambda(e.g.
> `Function f = e -> e + 1; assert 3 == f(2);`), and there
> are 2 ideas come to my mind:
> 
> 1) Generate a proxy implementing the FunctionInterface(e.g. `Function`,
> `Consumer`, etc.) and a `Callable` interface(maybe we should create a new
> one), the proxy will delegate invocations of `call(Object... args)` method
> of `Callable` to the method of the FunctionInterface. As you know, `f(2)` is
> actually `f.call(2)`, so we need the method `call`.
> 2) Transform `f(2)` to `f.apply(2)`, we can get the target method name by
> the type of FunctionInterface. To make it clear, let's have a look at two
> example: `Function`'s method is `apply`, `Consumer`'s method is `accept`.
> 
>   I prefer the first way, but I want to get advice from you :-)
> 
>   In addition, have you any idea about the issue[1]? If you do not know
> off the top of your head, I'll have to investigate when I have some spare
> time. Any help is appreciated!
> 
> Cheers,
> Daniel.Sun
> 
> [1]
> https://github.com/apache/groovy/blob/native-lambda/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java#L136
> 
> 
> 
> --
> Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html


Re: About the callable native lambda

2018-01-31 Thread Jesper Steen Møller
Hi list

FYI: This turned into a discussion of  the feature itself, on the GitHub commit 
thread. Basically, I'm proposing changing what "obj(params...)" means:
 - Non-SAM types: obj(params...) becomes obj.call(params...)
 - SAM types: obj(params...) becomes obj.(params...) - perhaps only 
as a fallback if obj.call doesn't exist.

This should be completely independent of how the lambda object itself was 
created.

I realize this is a potentially breaking change, but isn't it also a nice one?
Thoughts?

-Jesper

> On 31 Jan 2018, at 03.16, Daniel Sun  wrote:
> 
> Hi Jesper,
> 
> I think your suggestion is very nice and I've completed callable native
> lambda according to option 2 :-)
> 
> Here is the related commit:
> https://github.com/apache/groovy/commit/c24c0b7e6a67dcdf277207d4261cfa6f2b55031f
> 
> Cheers,
> Daniel.Sun
> 
> 
> 
> --
> Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html



Re: About the callable native lambda

2018-01-30 Thread Daniel Sun
Hi Jesper,

 I think your suggestion is very nice and I've completed callable native
lambda according to option 2 :-)

 Here is the related commit:
https://github.com/apache/groovy/commit/c24c0b7e6a67dcdf277207d4261cfa6f2b55031f

Cheers,
Daniel.Sun



--
Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html


Re: About the callable native lambda

2018-01-30 Thread Jesper Steen Møller
Hi Daniel

I very much recommend going with option 2: It could and should work for any 
functional interface, not just the ones implemented in Groovy.
Ideally, there would be no difference between a "Java lambda" and a "Groovy 
lambda"

-Jesper

> On 30 Jan 2018, at 01.14, Daniel Sun  wrote:
> 
> Hi all,
> 
>  I'm trying to implement the callable native lambda(e.g.
> `Function f = e -> e + 1; assert 3 == f(2);`), and there
> are 2 ideas come to my mind:
> 
> 1) Generate a proxy implementing the FunctionInterface(e.g. `Function`,
> `Consumer`, etc.) and a `Callable` interface(maybe we should create a new
> one), the proxy will delegate invocations of `call(Object... args)` method
> of `Callable` to the method of the FunctionInterface. As you know, `f(2)` is
> actually `f.call(2)`, so we need the method `call`.
> 2) Transform `f(2)` to `f.apply(2)`, we can get the target method name by
> the type of FunctionInterface. To make it clear, let's have a look at two
> example: `Function`'s method is `apply`, `Consumer`'s method is `accept`.
> 
>   I prefer the first way, but I want to get advice from you :-)
> 
>   In addition, have you any idea about the issue[1]? If you do not know
> off the top of your head, I'll have to investigate when I have some spare
> time. Any help is appreciated!
> 
> Cheers,
> Daniel.Sun
> 
> [1]
> https://github.com/apache/groovy/blob/native-lambda/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java#L136
> 
> 
> 
> --
> Sent from: http://groovy.329449.n5.nabble.com/Groovy-Dev-f372993.html