Re: About the callable native lambda
Hi again > On 1 Feb 2018, at 13.48, Jochen Theodorouwrote: > > [...] >> 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
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
(Sorry, first version went out too soon, thanks to the silly, silly Touch Bar) > On 31 Jan 2018, at 23.01, MGwrote: > > 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
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
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 Sunwrote: 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
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
- 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
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
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
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 Sunwrote: > > 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
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
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 Sunwrote: > > 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