You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@groovy.apache.org by Daniel Sun <re...@hotmail.com> on 2018/01/30 00:14:25 UTC
About the callable native lambda
Hi all,
I'm trying to implement the callable native lambda(e.g.
`Function<Integer, Integer> 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
Posted by Daniel Sun <re...@hotmail.com>.
Hi Jesper,
Given `f` is of SAM type, `f(params...)` is actually
`f.call(params...)`, which I transform to `f.<sam-method>(params...)`
eventually[1].
The reason why I do not choose `f.<sam-method>(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
Posted by Jesper Steen Møller <je...@selskabet.org>.
(Sorry, first version went out too soon, thanks to the silly, silly Touch Bar)
> On 31 Jan 2018, at 23.01, MG <mg...@arscreat.com> 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<String> { void run(){}; String get() { "Q" } }
def n(Runnable q1, Supplier <String> 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
Posted by Jesper Steen Møller <je...@selskabet.org>.
Hi again
> On 1 Feb 2018, at 13.48, Jochen Theodorou <bl...@gmx.org> 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<String> 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
Posted by Jochen Theodorou <bl...@gmx.org>.
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<String> { void run(){}; boolean test(String s) { s } }
>
> def n(Runnable q1, Predicate<String> q2, Q q2, q3) {
> r1()
> r2()
> r3()
> }
>
> m(new R(), new R(), new R())
you surely did mean:
>> def n(Runnable q1, Predicate<String> 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
Posted by Jesper Steen Møller <je...@selskabet.org>.
> On 31 Jan 2018, at 23.01, MG <mg...@arscreat.com> 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,
class Q implements Runnable, Predicate<String> { void run(){}; boolean test(String s) { s } }
def n(Runnable q1, Predicate<String> q2, Q q2, q3) {
r1()
r2()
r3()
}
m(new R(), new R(), new R())
> "...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.<sam-method>(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 <re...@hotmail.com> 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
Posted by MG <mg...@arscreat.com>.
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.<sam-method>(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 <re...@hotmail.com> 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
Posted by Jesper Steen Møller <je...@selskabet.org>.
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.<sam-method>(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 <re...@hotmail.com> 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
Posted by Daniel Sun <re...@hotmail.com>.
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
Posted by Jesper Steen Møller <je...@selskabet.org>.
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 <re...@hotmail.com> wrote:
>
> Hi all,
>
> I'm trying to implement the callable native lambda(e.g.
> `Function<Integer, Integer> 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
Posted by Daniel Sun <re...@hotmail.com>.
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
Posted by Remi Forax <fo...@univ-mlv.fr>.
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" <re...@hotmail.com>
> À: "dev" <de...@groovy.incubator.apache.org>
> 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<Integer, Integer> 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
Posted by Daniel Sun <re...@hotmail.com>.
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
Posted by Remi Forax <fo...@univ-mlv.fr>.
----- Mail original -----
> De: "Daniel Sun" <re...@hotmail.com>
> À: "dev" <de...@groovy.incubator.apache.org>
> 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