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

Post on 05-Jul-2015

506 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

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

top related