javantura v2 - all together now - making groovy and scala sing together - dinko srkoč
Upload: hujak-hrvatska-udruga-java-korisnika-croatian-java-user-association
Post on 05-Jul-2015
506 views
DESCRIPTION
Can two JVM languages, none of them Java, work harmoniously, side by side? Should they? In this lecture we’ll see how code written in Groovy and Scala work with Java and how they can be made to work with each other. The lecture is based on the experience of introducing Scala to an actual Groovy project.TRANSCRIPT
All Together Nowmaking Groovy and Scala sing together
Dinko Srkoč, Helix d.o.o.
1 / 33
Agenda1. Introduction
2. Communication with Java
Java & Groovy
Java & Scala
3. Communication without Java
Scala & Groovy
4. Trouble with Dependencies
2 / 33
1 / 33
Introductionmotivation, build systems, about Groovy/Scala
3 / 33
3 / 33
MotivationWhy would anyone use Groovy and Scala in the same project?
4 / 33
MotivationWhy would anyone use Groovy and Scala in the same project?
For us: using Akka in a Groovy project
concurrency
remoting + symmetric communication
5 / 33
Build SystemThe choice: Ant, Maven, ?
6 / 33
Build SystemThe choice: Ant, Maven, ?
The actual choice: SBT vs Gradle
7 / 33
Build SystemThe choice: Ant, Maven, ?
The actual choice: SBT vs Gradle
8 / 33
Build SystemThe choice: Ant, Maven, ?
The actual choice: SBT vs Gradle
supports Groovy and Scala equally well
easy to write build scripts
DSL in Groovy
(we were already using it)
9 / 33
Gradle example:
apply plugin: 'groovy'apply plugin: 'scala'
repositories { mavenCentral()}
dependencies { compile 'org.codehaus.groovy:groovy-all:2.3.7' compile "org.scala-lang:scala-library:2.11.4"}
// Scala: zinc based compiler - incremental compilationtasks.withType(ScalaCompile) { scalaCompileOptions.useAnt = false }
// order of compilation:// 1. Java// 2. Groovy// 3. ScalacompileScala.dependsOn(compileGroovy)
10 / 33
Communication with JavaGroovy & Java (match made in heaven)
Scala & Java (match made in Scala)
11 / 33
11 / 33
Java from GroovyA no-brainer
import java.util.List;
public interface IJava { Integer sum(List<Integer> xs);}
import org.apache.commons.lang3.RandomStringUtils as RndStr
class GroovyClass implements IJava { Integer id
Integer sum(List xs) { xs.sum() }
String rndStr() { RndStr.random(10) }}
12 / 33
Groovy from Javajoint compilation mode
class GroovyClass { Integer id def calc(a, Closure cl) { cl(a) }}
import groovy.lang.Closure;
class JavaClass { public static void main(String[] args) { GroovyClass gcl = new GroovyClass(); // in Groovy: new GroovyClass(id: 42)
gcl.setId(42); // in Groovy: gcl.id = 42 System.out.println("id=" + gcl.getId()); // in Groovy: gcl.id
gcl.calc(1, new Closure<Object>(null) { // in Groovy: gcl.calc(1) { a } Object doCall(Object a) { System.out.println(a); return a; } }); }}
13 / 33
About Joint Compilationresolves circular dependencies between Java, Groovy, and back again:
public class JavaClass {} // in JavaClass.java
class GroovyClass extends JavaClass {} // in GroovyClass.groovy
public class JavaClass2 extends GroovyClass {} // in JavaClass2.java
process:
1. Groovy compiler makes stubs from Groovy code
2. Groovy compiler puts Groovy stubs from step (1) on the classpath and
invokes Java compiler
3. Java compiler compiles Java code (Groovy dependencies resolved
from stubs)
4. Groovy compiler compiles Groovy code (Java dependencies resolved
from compiled Java code)
14 / 33
Java & GroovyGroovy tries to be as close to Java as possible:
similar syntax
uses Java's data structures
straightforward to use from Java
Groovy's Java API extensions can be used via DefaultGroovyMethods
dynamic meta progamming stuff not available from Java
traits (new feature in Groovy 2.3) awkward from Java
15 / 33
Java from Scalaimport java.util.List;
public interface IJava { Integer sum(List<Integer> xs);}public class JavaClass { public String aValue();}
import java.util.{ List ⇒ JList }
import org.apache.commons.lang3.{ RandomStringUtils ⇒ RndStr }
class ScalaClass(var id: Int) extends IJava {
override def sum(xs: JList[Integer]): Integer = { import scala.collection.JavaConverters._ // List(1, 2, 3).sum ... works, but // xs.sum ... doesn't
xs.asScala.foldLeft(0) { (acc, x) ⇒ acc + x } }
def calcVal(javaClass: JavaClass): String =
Option(javaClass.aValue()).map { s ⇒ change(s) } getOrElse "(undefined)"
16 / 33
def rndStr = RndStr.random(10)
Java from Scalaimport java.util.List;
public interface IJava { Integer sum(List<Integer> xs);}public class JavaClass { public String aValue();}
import java.util.{ List ⇒ JList }
import org.apache.commons.lang3.{ RandomStringUtils ⇒ RndStr }
class ScalaClass(var id: Int) extends IJava {
override def sum(xs: JList[Integer]): Integer = { import scala.collection.JavaConverters._ // List(1, 2, 3).sum ... works, but // xs.sum ... doesn't
xs.asScala.foldLeft(0) { (acc, x) ⇒ acc + x } }
def calcVal(javaClass: JavaClass): String =
Option(javaClass.aValue()).map { s ⇒ change(s) } getOrElse "(undefined)"
17 / 33
def rndStr = RndStr.random(10)
Java from Scalaimport java.util.List;
public interface IJava { Integer sum(List<Integer> xs);}public class JavaClass { public String aValue();}
import java.util.{ List ⇒ JList }
import org.apache.commons.lang3.{ RandomStringUtils ⇒ RndStr }
class ScalaClass(var id: Int) extends IJava {
override def sum(xs: JList[Integer]): Integer = { import scala.collection.JavaConverters._ // List(1, 2, 3).sum ... works, but // xs.sum ... doesn't
xs.asScala.foldLeft(0) { (acc, x) ⇒ acc + x } }
def calcVal(javaClass: JavaClass): String =
Option(javaClass.aValue()).map { s ⇒ change(s) } getOrElse "(undefined)"
18 / 33
def rndStr = RndStr.random(10)
Java from Scalaimport java.util.List;
public interface IJava { Integer sum(List<Integer> xs);}public class JavaClass { public String aValue();}
import java.util.{ List ⇒ JList }
import org.apache.commons.lang3.{ RandomStringUtils ⇒ RndStr }
class ScalaClass(var id: Int) extends IJava {
override def sum(xs: JList[Integer]): Integer = { import scala.collection.JavaConverters._ // List(1, 2, 3).sum ... works, but // xs.sum ... doesn't
xs.asScala.foldLeft(0) { (acc, x) ⇒ acc + x } }
def calcVal(javaClass: JavaClass): String =
Option(javaClass.aValue()).map { s ⇒ change(s) } getOrElse "(undefined)"
19 / 33
def rndStr = RndStr.random(10)
Scala from Javajoint compilation mode
class ScalaClass(var id: Int) {
def calc(a: Int, f: Int ⇒ Int): Int = f(a) def -> (x: Int): Int = x + 1}
import scala.runtime.AbstractFunction1;
public class JavaClass {
public static void main(String[] args) { ScalaClass scl = new ScalaClass(1);
scl.id_$eq(42); // in Scala: scl.id = 42 System.out.println("id = " + scl.id()); // in Scala: scl.id
int i = scl.$minus$greater(1); // in Scala: scl -> 1
scl.calc(new AbstractFunction1<Integer, Integer>() { public Integer apply(Integer i) { return i * 2; }
}); // in Scala: scl.calc(i ⇒ i * 2) }}
20 / 33
Scala from Javajoint compilation mode
class ScalaClass(var id: Int) {
def calc(a: Int, f: Int ⇒ Int): Int = f(a) def -> (x: Int): Int = x + 1}
import scala.runtime.AbstractFunction1;
public class JavaClass {
public static void main(String[] args) { ScalaClass scl = new ScalaClass(1);
scl.id_$eq(42); // in Scala: scl.id = 42 System.out.println("id = " + scl.id()); // in Scala: scl.id
int i = scl.$minus$greater(1); // in Scala: scl -> 1
scl.calc(new AbstractFunction1<Integer, Integer>() { public Integer apply(Integer i) { return i * 2; }
}); // in Scala: scl.calc(i ⇒ i * 2) }}
21 / 33
Scala from Javajoint compilation mode
class ScalaClass(var id: Int) {
def calc(a: Int, f: Int ⇒ Int): Int = f(a) def -> (x: Int): Int = x + 1}
import scala.runtime.AbstractFunction1;
public class JavaClass {
public static void main(String[] args) { ScalaClass scl = new ScalaClass(1);
scl.id_$eq(42); // in Scala: scl.id = 42 System.out.println("id = " + scl.id()); // in Scala: scl.id
int i = scl.$minus$greater(1); // in Scala: scl -> 1
scl.calc(new AbstractFunction1<Integer, Integer>() { public Integer apply(Integer i) { return i * 2; }
}); // in Scala: scl.calc(i ⇒ i * 2) }}
22 / 33
Scala from Javajoint compilation mode
class ScalaClass(var id: Int) {
def calc(a: Int, f: Int ⇒ Int): Int = f(a) def -> (x: Int): Int = x + 1}
import scala.runtime.AbstractFunction1;
public class JavaClass {
public static void main(String[] args) { ScalaClass scl = new ScalaClass(1);
scl.id_$eq(42); // in Scala: scl.id = 42 System.out.println("id = " + scl.id()); // in Scala: scl.id
int i = scl.$minus$greater(1); // in Scala: scl -> 1
scl.calc(new AbstractFunction1<Integer, Integer>() { public Integer apply(Integer i) { return i * 2; }
}); // in Scala: scl.calc(i ⇒ i * 2) }}
23 / 33
Java & ScalaScala uses Java libs easily, but guarding against null is common -
prevalence of Option on boundaries with Java
conversions between Java and Scala collections
Scala operators can have funny names in Java
(e.g. :: (cons) is seen as $colon$colon in Java)
Scala is semantically richer - some concepts are different or have no
equivalent in Java
declaration site variance vs use site variance (wildcards) annotations
abstract types (no equivalence in Java)
algebraic data types vs Java enums
Scala traits are awkward from Java
24 / 33
Communication without JavaGroovy <3 Scala ?
25 / 33
25 / 33
Scala & Groovyusing code from one language in another is no more difficult than using it
from Java
26 / 33
Scala & Groovyusing code from one language in another is no more difficult than using it
from Java
but
27 / 33
Scala & Groovyusing code from one language in another is no more difficult than using it
from Java
but
having circular dependencies is problematic
because there is no joint compilation
28 / 33
Trouble with Dependenciesjoint compilation - not!
29 / 33
29 / 33
Example (1/2)order of compilation (defined in Gradle build script):
1. Groovy
2. Scala
class GroovyClass { def process() { def scl = new ScalaClass() // depends on not yet compiled scl.calc(42) // scala code }}
class ScalaClass { def calc(Int i): String = ???}
How to compile the above code?
30 / 33
Example (2/2)a clumsy solution (a.k.a. a hack)
class ScalaClass { String calc(int i) { throw new Exception("This is a stub. Not to be used!") }}
class GroovyClass { def process() { def scl = new ScalaClass() // Groovy compiler sees scl.calc(42) // ScalaClass as Groovy code }}
class ScalaClass { // Scala compiler overrides def calc(Int i): String = ??? // Groovy's ScalaClass}
31 / 33
Summarywith API designed for interoperability Groovy and Scala
can sing harmoniously (more or less)
avoid circular dependencies if possible
Pain points
no joint compile mode other than with Java
different data structures - needs conversion
functional expressions of one language translate to anonymous class
construction in other
different programming idioms can cause more verbose code on the other
side
32 / 33
Thank you!
Q & A
Slideshow created with remark.
Drawings by yours truly.
33 / 33