javantura v2 - all together now - making groovy and scala sing together - dinko srkoč

33
All Together Now making Groovy and Scala sing together Dinko Srko č , Helix d.o.o. 1 / 33

Category:

Technology


1 download

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

Page 1: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

All Together Nowmaking Groovy and Scala sing together

Dinko Srkoč, Helix d.o.o.

1 / 33

Page 2: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Agenda1. Introduction

2. Communication with Java

Java & Groovy

Java & Scala

3. Communication without Java

Scala & Groovy

4. Trouble with Dependencies

2 / 33

1 / 33

Page 3: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Introductionmotivation, build systems, about Groovy/Scala

3 / 33

Page 4: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

3 / 33

MotivationWhy would anyone use Groovy and Scala in the same project?

4 / 33

Page 5: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 6: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Build SystemThe choice: Ant, Maven, ?

6 / 33

Page 7: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Build SystemThe choice: Ant, Maven, ?

The actual choice: SBT vs Gradle

7 / 33

Page 8: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Build SystemThe choice: Ant, Maven, ?

The actual choice: SBT vs Gradle

8 / 33

Page 9: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 10: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 11: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Communication with JavaGroovy & Java (match made in heaven)

Scala & Java (match made in Scala)

11 / 33

Page 12: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 13: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 14: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 15: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 16: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 17: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 18: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 19: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 20: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 21: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 22: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 23: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 24: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 25: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Communication without JavaGroovy <3 Scala ?

25 / 33

Page 26: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

25 / 33

Scala & Groovyusing code from one language in another is no more difficult than using it

from Java

26 / 33

Page 27: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Scala & Groovyusing code from one language in another is no more difficult than using it

from Java

but

27 / 33

Page 28: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 29: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Trouble with Dependenciesjoint compilation - not!

29 / 33

Page 30: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 31: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 32: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

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

Page 33: Javantura v2 - All Together Now - making Groovy and Scala sing together - Dinko Srkoč

Thank you!

Q & A

Slideshow created with remark.

Drawings by yours truly.

33 / 33