Search code examples
javascalagroovyimplicit-conversionpolyglot

Implicitly convert groovy type to pass to a fixed non-groovy method


I am writing a scala application which loads up Groovy "plugin" classes at runtime. Once the plugins are loaded, standard scala types (like List and Option) are passed into them for processing.

Groovy naturally doesn't have syntactic sugar for scala types (particularly the Function* family), but I'd like a similar easy syntax. The best I have right now is to use the as operator to coerce groovy closures into scala types, e.g:

List<String> list = ... // Scala list
list.map({ it.toUpperCase() } as Function1<String,String>)

It would be nice to not have to include the as ... tail every time as it's bigger than the actual closure.

This question suggests changing the receiving method to not specify the parameter type, however that only works when it's a groovy method you can control. I'm using compiled scala/java classes.

I'm aware that there are various means for implicitly converting things in Groovy, however there are many small isolated groovy scripts being loaded independently. I don't know groovy well enough to understand which mechanism best suits this architecture so that all loaded plugins get this implicit conversion with minimal ceremony and imports. I have about 50+ plugins so streamlining this scala/groovy interop is worth it to me.

This is a simplified version of how I'm loading the groovy scripts (where Plugin is one of my types):

// Created once
val currentClassLoader = this.getClass.getClassLoader
val groovyClassLoader = new GroovyClassLoader(currentClassLoader)

...

// Many plugins created this way
val pluginFile = new java.io.File("path/to/some/plugin/MyPlugin.groovy")
val plugin: Plugin = groovyClassLoader.parseClass(pluginFile).asInstanceOf[Plugin]

// Use it
val list = List(1,2,3)
val answer = plugin.process(list)

Thanks in advance!

-Rohan


Solution

  • I checked the bytecode of Function1 scala produces and it is an interface, but by far not one with a single abstract method. This means it is useless for SAM conversion, ie. Java 8 would not be able to use lambdas to autoconvert to that. I also checked AbstractFunction1, and while it is an abstract class, it has no abstract method, so even that useless for extended SAM conversion as Groovy does it.

    The only solution I see would be to write an extension module which does the conversion.

    EDIT: Maybe scala-java8-compat can help. It targets allowing java8 lambdas for scala FunctionN, which would also allow SAM conversion for open blocks in case of Groovy 2.2.0 +