About the callable native lambda

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
14 messages Options
12
Reply | Threaded
Open this post in threaded view
|

About the callable native lambda

Daniel Sun
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
Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

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 <[hidden email]> 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

Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

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
Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

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.<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 <[hidden email]> 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

Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

Remi Forax
In reply to this post by Daniel Sun
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" <[hidden email]>
> À: "dev" <[hidden email]>
> 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
Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

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
Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

Remi Forax
In reply to this post by Daniel Sun


----- Mail original -----
> De: "Daniel Sun" <[hidden email]>
> À: "dev" <[hidden email]>
> 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
Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

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
MG
Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

MG
In reply to this post by Jesper Steen Møller
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 <[hidden email]> 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
>

Reply | Threaded
Open this post in threaded view
|

Re: About the callable native lambda

Daniel Sun
In reply to this post by Jesper Steen Møller
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
12