0

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

Community
  • 1
  • 1
rmin
  • 1,018
  • 1
  • 9
  • 18
  • did you try the code without `as Function1`? Function1 is not functional interface in the Java sense, since it is no interface, but it looks to me still like a SAM type, which groovy allows you to auto-convert to, even if it is no interface. So without having experience with scala and scala.Function1 I think it might just work without explicit conversion. – blackdrag May 15 '15 at 16:34
  • @blackdrag Thanks for the info - it fails without the `as ...`. Something like `lines.map({ it.toUpperCase() })` throws a `MissingMethodException`: "No signature of method: scala.collection.mutable.ArrayBuffer.map() is applicable for argument types: ..." – rmin May 18 '15 at 15:15
  • what groovy version are you using? – blackdrag May 19 '15 at 00:15
  • @blackdrag 2.3.9 - this is what my sbt file includes as a dependency: "org.codehaus.groovy" % "groovy-all" % "2.3.9", – rmin May 20 '15 at 04:27

1 Answers1

0

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 +

blackdrag
  • 6,413
  • 2
  • 26
  • 38
  • Thanks for all your help and the resources - I'll follow them up. I was aware of extension modules and other means of metaprogramming. I was hoping for advice as to which mode best fits my plugin style architecture. From what you're saying though, it seems like extension modules are the *only* way to do this, is that right? – rmin May 23 '15 at 05:39
  • Of course you can add the methods by other means, like runtime meta programming, but extension modules seem to me more practical in this situation. For example you won't have to pay the penalty for using ExpandoMetaClass. – blackdrag May 24 '15 at 06:11