Was that the one case when moaning about other people's work is not frowned upon? Damn, I wish I knew, I would unleash my moaning in public!
But seriously, this limitation is very annoying when you work with Ratpack in Groovy. I have moved towards more type safe coding recently and when I learned about SAM coercion I started using SAM functional interfaces in signatures of methods and then passing closures to these methods. That way it's easier to figure out what a method parameter takes and returnes without having to look at method implementation. So any improvements in that area are most welcome. On Wed, 18 Jan 2017 at 11:04, Cédric Champeau <cedric.champ...@gmail.com> wrote: > This is something that we can improve. We knew it when we implemented SAM > type coercion, and decided to wait until someone complains, to see how > often this use case happens :) > > 2017-01-18 11:59 GMT+01:00 Andres Almiray <aalmi...@gmail.com>: > > Hello everyone, > > Just yesterday Greg L. Turnquist blogged about an usage pattern of > Spinnaker, Cloud Foundry, and Groovy. See > http://greglturnquist.com/2017/01/reactively-talking-to-cloud-foundry-with-groovy.html > > Basically he complains that Groovy can't coerce a Closure to a given SAM > type. In his own words > > "Groovy has this nice feature where it can coerce objects. However, with > all the overloading, Groovy gets lost and can’t tell which TupleUtils > function to target." > > Now I know that based on historical reasons Groovy did not coerce closures > into SAMs until very "recently" (was it 2.2?). We also gained @DelegatesTo > in order to supply additional hints to the compiler (@CompileStatic and > @TypeChecked) and IDEs. Despite all this Groovy does not offer a "clean" > solution to automatically coerce a closure into a SAM. > > Take for example the following Java code: > > ---- > public interface Function1 { void call(String arg0); } > > public interface Function2 { void call(String arg0, String arg1); } > > import groovy.lang.DelegatesTo; > public class API { > public void doit(@DelegatesTo Function1 func) { > System.out.println("Invoking "+ func.toString()); > func.call("arg0"); > } > > public void doit(@DelegatesTo Function2 func) { > System.out.println("Invoking "+ func.toString()); > func.call("arg0", "arg1"); > } > } > ----- > > Invoking an instance of API from Groovy > > ---- > class Main { > static void main(String[] args) { > API api = new API() > api.doit({ String arg0 -> println "Received $arg0" }) > api.doit({ String arg0, String arg1 -> println "Received $arg0 > $arg1" }) > } > } > ---- > > Results in a runtime exception such as > > Exception in thread "main" groovy.lang.GroovyRuntimeException: Ambiguous > method overloading for method sample.API#doit. > Cannot resolve which method to invoke for [class > sample.Main$_main_closure1] due to overlapping prototypes between: > [interface sample.Function1] > [interface sample.Function2] > at > groovy.lang.MetaClassImpl.chooseMostSpecificParams(MetaClassImpl.java:3263) > at > groovy.lang.MetaClassImpl.chooseMethodInternal(MetaClassImpl.java:3216) > at groovy.lang.MetaClassImpl.chooseMethod(MetaClassImpl.java:3159) > at > groovy.lang.MetaClassImpl.getMethodWithCachingInternal(MetaClassImpl.java:1336) > at > groovy.lang.MetaClassImpl.createPojoCallSite(MetaClassImpl.java:3391) > at > org.codehaus.groovy.runtime.callsite.CallSiteArray.createPojoSite(CallSiteArray.java:132) > at > org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallSite(CallSiteArray.java:166) > at > org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) > at > org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) > at > org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) > at sample.Main.main(Main.groovy:6) > > Activating static type checking yields > > /private/tmp/foo/src/main/groovy/sample/Main.groovy: 7: [Static type > checking] - Reference to method is ambiguous. Cannot choose between [void > sample.API#doit(sample.Function1), void sample.API#doit(sample.Function2)] > @ line 7, column 9. > api.doit({ String arg0 -> println "Received $arg0" }) > ^ > > /private/tmp/foo/src/main/groovy/sample/Main.groovy: 8: [Static type > checking] - Reference to method is ambiguous. Cannot choose between [void > sample.API#doit(sample.Function1), void sample.API#doit(sample.Function2)] > @ line 8, column 9. > api.doit({ String arg0, String arg1 -> println "Received $arg0 > $arg1" }) > > Clearly Groovy requires some hints to determine the first closure must be > coerced to Function1 and the second to Function2. Not even @DelegatesTo > helps in this case. > > The current "workaround" (from a Java dev POV anyway) is to sprinkle the > code with usages of the 'as' keyword to explicitly coerce a closure into a > target type. And this is exactly where the Java interop history breaks due > to Java 8, because as we know Java 8 lambda expressions are automatically > coerced into a matching SAM type. > > Does the parrot parser offer an alternative to this problem? > > Can the compiler be aware of additional arg/type information provided by > the Closure to figure out the right SAM type to use? > > Cheers, > Andres > > ------------------------------------------- > Java Champion; Groovy Enthusiast > http://jroller.com/aalmiray > http://www.linkedin.com/in/aalmiray > -- > What goes up, must come down. Ask any system administrator. > There are 10 types of people in the world: Those who understand binary, > and those who don't. > To understand recursion, we must first understand recursion. > > > > > > > >