Comparing JVM languages

69
Comparing JVM languages José Manuel Ortega Candel | @jmortegac 26-27 June 2015

Transcript of Comparing JVM languages

Comparing JVM languages

José Manuel Ortega Candel | @jmortegac

26-27 June 2015

INDEX

Functional programming and closures 2

Language classification 1

Java 8 new features 3

Scala, Groovy, Kotlin, Clojure and Ceylon 4

Advantages and drawbacks 5

Comparing features 6

Language classification

FUNCTIONAL NOT FUNCTIONAL

STATIC

DYNAMIC

Functional programming

•Data structure persistent and immutable

•Closures

•Higher order functions •Pass functions to functions as parameter •Create functions within functions •Return functions from other functions

•Functions as first class values

Closures

•Reduce amount of “boiler plate” code

•Eliminate the boilerplate code of anonymous inner classes

•Allow the use of idiomatic functional programming code

•Allow the creation of custom control structures

• In Java 8 closures are just syntactic sugar for defining anonymous inner classes

Java 8 new features

Lambda expressions

Closures

Streams

Enhaced collections

Processing data in parallel

Pattern Filter-Map-Reduce

Annotations types

Methods References

(String s)-> System.out.println(s)

System.out::println

Streams

Filter-Map-Reduce

Book book=new Book(123455,"Beginning Java 8");

Book book1=new Book(3232332,"Java 8 Advanced");

Book book2=new Book(43434343,"Groovy Advanced");

List<Book> books=Arrays.asList(book,book1,book2);

Predicate<Book> condition=(it) -> it.getTitle().contains("Java");

String jsonFormat=books

.stream()

.filter(condition)

.map(Book::toJSON)

.collect(Collectors.joining(", "));

[{isbn:123455,title:'Beginning Java 8'}, {isbn:3232332,title:'Java 8 Advanced'}]

Parallel Streams

List myList = .. Stream stream = myList.stream();

Stream parallelStream = myList.parallelStream();

Stream parallel = stream.parallel();

• Streams are sequential by default

• With Parallel you can optimize number of threads

• More efficient with bigger collections

Sequential vs Parallel

books.parallelStream().filter((it) -> it.getTitle().contains("Java")).

forEach((it)->System.out.println(it+" "+Thread.currentThread()));

Book{isbn=3232332, title='Java 8 Advanced'} Thread[main,5,main]

Book{isbn=123455, title='Beginning Java 8'} Thread[main,5,main]

books.stream().filter((it) -> it.getTitle().contains("Java")).

forEach((it)->System.out.println(it+" "+Thread.currentThread()));

Book{isbn=3232332, title='Java 8 Advanced'} Thread[main,5,main]

Book{isbn=123455, title='Beginning Java 8'} Thread[ForkJoinPool.commonPool-

worker-2,5,main]

Scala

Combines the paradigms of functional programming and object-oriented

Statically typed No types primitives like Java

Everything is an Object, including functions

Performance equivalent to Java

Scala features

Pattern matching

Closures,lambdas,anonymous functions

Traits and mixins for multiple inheritance

Operator overloading

Support Data Paralell like Java 8

Concurrency models(Akka,actors)

Inmutable and mutable data structures

Scala examples

def factorial(n : Int): Int = n match{

case 0 => 1

case x if x >0 => factorial(n-1) *n

}

val languages = List("Scala", "Groovy", "Clojure", "Kotlin")

val headList = languages.head // Scala

val tailList = languages.tail; //List( "Groovy", "Clojure", "Kotlin")

val empty = languages.isEmpty //false

def orderList(xs: List[String]): List[String] =

if (xs.isEmpty) Nil

else insert(xs.head, isort(xs.tail)

@BeanProperty

Annotation which is read by the Scala

compiler to generate getter and setter

class Bean {

@scala.reflect.BeanProperty

var name: String

}

val someNumbers = List(-11, -10, -5, 0, 5, 10)

someNumbers.filter((x) => x > 0)

someNumbers.filter(_ > 0)

List[Int] = List(5, 10)

scala.collection.immutable

Inmutability in Scala

scala> var languages = Set("Scala", "Kotlin")

languages : scala.collection.immutable.Set[java.lang.String] =

Set(Scala, Kotlin)

scala> languages += "Clojure"

scala> languages

res: scala.collection.immutable.Set[java.lang.String] =

Set(Scala, Kotlin, Clojure)

scala> languages -= " Kotlin "

scala> languages

res: scala.collection.immutable.Set[java.lang.String] =

Set(Scala, Clojure)

• You can add elements in a inmutable collection

Java 8 vs Scala

Java 8 vs Scala streams

bookList.parallelStream().filter(b -> b.title.equals(”Clojure”))

.collect(Collectors.toList())

bookList.par.filter(_.title == “Clojure”)

bookList.par.filter( b =>b.title == “Clojure”)

val bookFilter = bookList.par.filter(_.title.contains("Groovy"))

.foreach{i=> println(Thread.currentThread) } Thread[ForkJoinPool-1-worker-3,5,main]

Thread[ForkJoinPool-1-worker-5,5,main]

Kotlin

Object-oriented with functional elements

Developed by JetBrains

Statically typed like Scala Smarts casts

Elvis operator(?) for checking null like Groovy

Type inference

100 % interoperability with Java

Kotlin syntax & rules

Inmutable/Mutable variables

No new keyword for create objects

No primitive types

No static members

Primary constructors

No fields, just properties

By default, all classes are final

Kotlin

Smart casts Maps

val languages = mapOf("Java" to "Gosling",

"Scala" to "Odersky","Groovy" to "Strachan")

for((language,author) in languages){

println("$author made $language")

}

fun eval(e: Expr): Double = when (e) {

is Num-> e.value.toDouble()

is Sum -> eval(e.left) + eval(e.right)

else ->

throw IllegalArgumentException("Unknown

expression")

}

val mapLanguages = hashMapOf<String, String>()

mapLanguages.put("Java", "Gosling")

mapLanguages.put("Scala", "Odersky")

for ((key, value) in mapLanguages) {

println("key = $key, value = $value")

}

fun patternMatching(x:Any) {

when (x) {

is Int -> print(x)

is List<*> ->{print(x.count())}

is String -> print(x.length())

!is Number -> print("Not even a number")

else -> print("can't do anything")

}

}

Kotlin

Operator overloading Default & name arguments

println(product(2))

println(product(2,3))

println(format(text="kotlin"))

println(format(text="kotlin", upperCase= true))

2

6

kotlin

KOTLIN

fun product(a:Int,b:Int=1):Int{

return a.times(b);

}

fun format(text:String,upperCase:Boolean=

false):String {

return if (upperCase) text.toUpperCase() else text

}

Kotlin

Iterators fun iterateOverCollection(collection: Collection<Int>) {

for (element in collection) {}

}

fun iterateOverString() {

for (c in "abcd") {}

"abcd".iterator()

}

fun iteratingOverMap(map: Map<Int, String>) {

for ((key, value) in map) {}

}

Kotlin

Data classes Data annotation changes behavior of hashCode, equals, toString functions

public class Book (var isbn:Int, var title:String)

fun main(args: Array<String>) {

//Books

val book = Book(123456, "Beginning Kotlin")

val book2 = Book(123456, "Beginning Kotlin")

println(book);

println(book.hashCode());

println(book2.hashCode());

println(book.equals(book2));

if (book == book2)

println("Same Book");

if (book != book2)

println("Diferent Book");

}

books.Book@2a84aee7

713338599

168423058

false

Diferent Book

data public class Book (var isbn:Int, var title:String)

fun main(args: Array<String>) {

//Books

val book = Book(123456, "Beginning Kotlin")

val book2 = Book(123456, "Beginning Kotlin")

println(book);

println(book.hashCode());

println(book2.hashCode());

println(book.equals(book2));

if (book == book2)

println("Same Book");

if (book != book2)

println("Diferent Book");

Book(isbn=123456,

title=Beginning Kotlin)

1848623012

1848623012

true

Same Book

Kotlin

Classes constructor • It is not necessary define a constructor like in Java

• You can use the class definition to pass the properties and

default values data public class Book (var isbn:Int, var title:String="My Default Book")

fun main(args: Array<String>) {

val book = Book(123456)

println(book.title);

}

Kotlin

Higher order functions • Lambda expressions

• it implicit parameter

val filter = books filter({ b: Book -> b.title.contains(“Kotlin") })

val book = Book(123455, "Beginning Java 8")

val book1 = Book(3232332, "Java 8 Advanced")

val book2 = Book(43434343, “Kotlin Advanced")

val books = Arrays.asList(book, book1,book2)

Filter[Book(isbn=43434343, title=Kotlin Advanced)]

val filter = books filter({ it.contains(“Kotlin") })

Kotlin collections

val listInmutable

=listOf("Java","Kotlin","Groovy","Scala")

val listMutable=ArrayList<String>()

listMutable.add("Java")

listMutable.add("Kotlin")

Kotlin vs Java list iterate

for ((index,element) in list.withIndex()) {

println(“$index$element”);

}

int index=0

for (String element:list) {

System.out.println(index+””+element);

index++

}

Kotlin smart cast vs Java

if (expr is Number) {

println(expr.getValue());

//expr is automatically cast to Number

}

if (expr instanceof Number) {

System.out.println((Number)expr).getValue());

}

Kotlin vs Java types inference

val longJavaClassName = LongJavaClassName()

LongJavaClassName longJavaClassName =new LongJavaClassName()

Groovy

Java supercharged(syntactic sugar)

Object oriented and dynamic language

Optional typing like Scala and Kotlin

All type checking is at run time

It reduces Java boilerplate code including no

semi colons, no getters/setters, type

inference, null safety, elvis operators

Groovy

Closures with lambda

or it implicit parameter

def upper = { s-> s.toUpperCase()}

def upper = {it.toUpperCase()}

Collections

def languages = ["Java":”James

Gosling”, "Scala":”Martin Odersky”,

"Clojure":”Rich Hickey”,

"Ceylon":”Gavin King”,

"Groovy":”James Strachan”]

languages.each { entry -> println}

languages.each { k,v -> println}

Methods as closures with .& operator

def object

object = 2

object = true

object = [1,2,3]

Optional typing

Groovy

@Memoized annotation @Inmutable annotation

@Memoized

def BigInteger fibRecursiveMemoized(n) {

if (n<2) return 1

else return fibRecursiveMemoized(n-1) +

fibRecursiveMemoized(n-2)

}

TimeIt.code{println(fibonnaci.fibRecursive(40))}

TimeIt.code{println(fibonnaci.fibRecursiveMemoized(40))}

165580141

Time taken 42.083165464 seconds

165580141

Time taken 0.061408655 seconds //with memoize

import groovy.transform.Immutable

@Immutable class Language {

String title;

Long isbn;

}

def l1 = new Language (title:'Beginning Groovy',isbn:100)

def l2 = new Language (title:'Groovy advanced',isbn:200)

l1.title = 'Groovy for dummies'

// Should fail with groovy.lang.ReadOnlyPropertyException

Groovy

Time execution

class TimeIt {

def static code(codeBlock){

def start = System.nanoTime()

try{

codeBlock()

}finally{

def end = System.nanoTime()

println "Time taken ${(end-start)/1e9} seconds"

}

}

}

Groovy

Parsing and creating XML

Groovy Advantages Disadvantages

Java Collections Non-lazy evaluation

Closures

(like Lambda expressions in Java 8)

It's performance isn't great

(but it's improving)

Filter – Map – Reduce in streams

Easy interoperability with Java

Static and dynamic typing

@TypeChecked

@CompileStatic

Metaprogramming

Java Groovy

def languages = ["Java", "Scala", "Clojure",

"Kotlin", "Ceylon","Groovy"]

def filter = languages.stream()

.findAll { it.size()>5 }

.collect { it.toUpperCase()}

.first()

List<String> languages = Arrays.asList("Java",

"Scala", "Clojure", "Kotlin","Ceylon","Groovy");

Optional<String> bookFiltered = languages.stream()

.filter(s -> s.length()>5 )

.map(s -> s.toUpperCase())

.findFirst();

Clojure

Pure functional language

JVM language based in Lisp

Not Object Oriented

Dynamically typed

All data structure including list ,vectors, maps,

sequences, collections, are inmutable and fully

persistent

The main data structure is the list

The key is the use of High Order Functions

Clojure

Alternate implementations in functions (defn square-or-multiply

"squares a single argument, multiplies two arguments"

([] 0) # without arguments

([x] (* x x)) # one argument

([x y] (* x y))) # two arguments

List operations

(first '(:scala :groovy :clojure))return scala

(rest '(:scala :groovy :clojure))return all elements excepts the first

(cons :kotlin '(:scala :groovy :clojure))add kotlin to head in the list

Clojure

Sequences (defn count-down [n]

(if (<= n 0)

'(0)

(cons n (lazy-seq (count-down (dec n))))))

Filter – Map - Reduce

(filter integer? [1 2.71 3.14 5 42]) => (1 5 42)

(map (fn [n] (* n n)) [1 2 3 4 5]) => (1 4 9 16 25)

(reduce + [1 2 3 4]) => 10

user=> (count-down 8)

(8 7 6 5 4 3 2 1 0)

Clojure

Records

(defrecord Book [isbn title])

(def bookList [(Book. 432423 "Clojure")(Book. 534243 "Kotlin")

(Book. 432424 "Groovy")])

(println "count bookList "(count bookList))

(dorun (for [item bookList]

(println (:title item)(:isbn item))))

(println (filter #(= "Clojure" (get % :title)) bookList))

count bookList 3

Clojure 432423

Kotlin 534243

Groovy 432424

(#user.Book{:isbn 432423, :title

Clojure})

(defrecord Object [prop1 propn])

Clojure

Recursive functions with trampoline and memoize

(defn factorial ([x] (trampoline (factorial (dec x) x)))

([x a] (if (<= x 1) a #(factorial (dec x) (*' x a)))))

(def fibonaci

(memoize (fn [n]

(if (< n 2)

n

(+ (fibonaci (dec n))

(fibonaci (dec (dec n))))))))

(time(println (factorial 20)))

(time(println (fibonaci 30)))

"Elapsed time: 0.692549 msecs"

832040

"Elapsed time: 1.263175 msecs"

"Elapsed time: 24.574218 msecs"

832040

"Elapsed time: 118.678434 msecs"

2432902008176640000

Clojure Advantages Disadvantages

Very simple Not Object oriented

The language has little elements

compared with other languages

Difficult to decipher stacktraces and errors

Excellent concurrency support Requires a new way to solve problems

Easy interoperability with java There are no variables

Powerful language features

(macros,protocols)

Thre is no inheritance concept

Clojure programs run very quickly No mecanism for avoid NullPointerException

JVM is highly optimized

Clojure vs Java

Code reducing

(defn blank [str]

(every#? (Character/isWhitespace %)

str))

public class StringUtils {

public static boolean isBlank(String str) {

int strLen;

if (str == null || (strLen = str.length()) == 0) {

return true;

}

for (int i = 0; i < strLen; i++) {

if ((Character.isWhitespace(str.charAt(i)))) {

return true;

}}

return false;

}}

Ceylon

Is very similar to Java, reducing verbosity

Object oriented with functional features

Strong static typing with type inference

Explicit module system

Comparing with Java

Eliminates static, public, protected, private

Add new modifiers like variable, shared, local

Shared is used for make visibility outside in other

modules(like public in Java)

Arrays are replaced by sequences

Ceylon

Sequence languages = Sequence("Java",

"Groovy","Clojure","Kotlin");

//Check if the sequence is empty

if(nonempty languages){

String? firstElementValue = languages.value(1);

String? firstElement = languages.first;

}

Sequence more =

join(languages,Sequence("Ceylon","Scala"));

Sequences Generics & Union types

Iterable<String|Boolean> list =

{“string”,true,false}

ArrayList<Integer|Float> list =

ArrayList<Integer|Float>

(1, 2, 1.0, 2.0)

Comparing featues

• Reducing verbosity

• Variables definition

• Null-safety

• Optional types

• Lambdas

• Pattern matching

• Traits

• Java interoperability

Reducing Java verbosity Reducing Boilerplate Code

class MyClass{

private int index;

private String name;

public MyClass(int index, String name){

this.index = index;

this.name = name;

}

}

case class MyClass(index: Int, name: String)

public class MyClass(var index:Integer,var name:String){

}

class MyClass {

int index

String name

}

class MyClass(index,name) {

shared Integer index;

shared String name;

}

(defstruct MyClass :index :name)

Variables definition

• In Kotlin and Scala you can define 2 types • var myMutableVariable

• val myInmutableVariable

• In Ceylon we have variable modifier for mutable

variable Integer count=0;

count++; //OK

Integer count=0;

count++;//ERROR

val name: Type = initializer // final “variable”

var name: Type = initializer // mutable variable

Null-safety

• Operator ? used in Kotlin,Groovy,Ceylon

• Avoid NullPointerException

var b : String? = "b"

b = null // valid null assignment

val l = b?.length()

Elvis Operator(?:) and Safe navigation (?.)

def displayName = user.name ? user.name : "Anonymous“

def streetName = user?.address?.street

String? argument = process.arguments.first;

if (exists argument) { //checking null values

}

Optional Types

Optional<Object> optional = findObject(id);

//check if Optional object has value

if (optional.isPresent()) {

System.out.println(optional.get());

}

Type safe Option[T] in Scala

New class java.util.Optional

Optional Type in Scala

private val languages = Map(1 -> Language(1, "Java", Some("Gosling")),

2 -> Language(2, "Scala", Some("Odersky")))

def findById(id: Int): Option[Language] = languages.get(id)

def findAll = languages.values

def main(args: Array[String]) {

val language = Optional.findById(1)

if (language.isDefined) {

println(language.get.name)

}

}

val language = Optional.findById(3) None

Optional Type in Scala

val languages =Map("Java"->"Gosling","Scala"->"Odersky")

languages:scala.collection.immutale.Map[java.lang.String,java.lang.String]

languages get "Scala"

Option[java.lang.String] = Some(Odersky)

languages get "Groovy"

Option[java.lang.String] = None

Lambdas in streams

List<Book> books=Arrays.asList(book,book1,book2);

Optional<String> bookFiltered =

books.stream()

.filter(b -> b.getTitle().contains("Groovy"))

.map(b -> b.getTitle().toUpperCase())

.findFirst();

val books = Arrays.asList(book,book1,book2)

val bookFiltered : String? =

books.sequence()

.filter { b->b.title.contains("Groovy")}

.map { b->b.title.toUpperCase() }

.firstOrNull()

def bookfilter= books.

findAll { b->b.title.contains(" Groovy ") }

.sort { b->b.isbn }

.collect { b->b.title.toUpperCase() }

.find()

val bookList = List(book1,book2,book3);

val bookFilter = bookList

.filter(b=>b.title.contains("Groovy"))

.map(book => book.title)

Pattern matching

• Is a generalization of Java switch/case

def generalSize(x: Any) = x match {

case s: String => s.length

case m: Map[_, _] => m.size

case _ => -1

}

scala> generalSize("abc")

res: Int = 3

scala> generalSize(Map(1 -> 'a', 2 -> 'b'))

res: Int = 2

fun fib(n: Int): Int {

return when (n) {

1, 2 -> 1

else -> fib(n - 1) + fib(n - 2)

}

}

fun patternMatching(x:Any) {

when (x) {

is Int -> print(x)

is List<*> ->{print(x.size())}

is String -> print("String")

!is Number -> print("Not even a number")

else -> print("can't do anything")

}

}

Traits vs Java interfaces

Traits allow declare method definitions

A trait is an interface with an implementation(behavior and/or state)

Traits support “multiple inheritance”

One class can inherit from many traits

Traits.Code in interfaces

trait Publication {

int number

def getNumberPublication(){

return (number)

}

}

trait Location {

String description

def getInfoPublication(){

return (description)

}

}

class MyBook implements Publication,Location{

Integer isbn

String title

def getInfoBook(){

return (isbn+ " "+title)

}

}

trait Publication {

def description() = println(“Description”)

}

class Book(var isbn:Int,var title:String) extends Publication{

def getInfoBook(){

return (isbn+ " "+title)

}

}

trait Publication{

var number:String

fun getNumberPublication(){

println(number)

}

}

class Book:Publication {

}

Java interoperability object list extends App {

import java.util.ArrayList

var list : ArrayList[String] = new ArrayList[String]()

list.add("hello")

list.add("world")

println(list)

}

import collection.JavaConversions._

java.util.Arrays.asList(1,2,3).

foreach(i =>println(i))

java.util.Arrays.asList(1,2,3).

filter(_ % 2 ==0)

fun list(source: List<Int>):ArrayList<Int> {

val list = ArrayList<Int>()

for (item in source)

list.add(item)

for (i in 0..source.size() - 1)

list[i] = source[i]

return list

}

(ns clojure-http-server.core

(:require [clojure.string])

(:import (java.net ServerSocket SocketException)

(java.util Date)

(java.io PrintWriter BufferedReader

InputStreamReader BufferedOutputStream)))

Java interoperability

import scala.beans.BeanProperty

class Language(

@BeanProperty var name: String,

@BeanProperty var author: String) {

}

public static void main(String[] args) {

Language l1 = new Language(“Java",”Gosling”);

Language l2 = new Language(“Scala",”Odersky”);

ArrayList<Language> list = new ArrayList<>();

list.add(l1);

list.add(l2);

}

List Partitions

• We can use partition over a list for obtain 2 lists

scala> List(1, 2, 3, 4, 5) partition (_ % 2 == 0)

res: (List[Int], List[Int]) = (List(2, 4),List(1, 3, 5))

// partition the values 1 to 10 into a pair of lists of even and odd numbers

assertEquals(Pair(listOf(2, 4, 6, 8, 10), listOf(1, 3, 5, 7, 9)), (1..10)

.partition{ it % 2 == 0 })

Read file and print lines import scala.io.Source

for (line <- Source.fromFile(“file.txt”).getLines())

println(line.length +" "+ line)

import java.nio.file.*;

import java.util.stream.Stream;

Path dict = Paths.get(“file.txt”);

Stream<String> linesStream = Files.lines(dict);

linesStream.forEach(System.out::println);

val dict = Paths.get(“file.txt")

val linesStream = Files.lines(dict)

val lines = linesStream.forEach{println(it)};

Read file and print lines

(defn read-file [file]

(with-open [rdr (clojure.java.io/reader file)]

(doseq [line (line-seq rdr)]

(println line))))

(read-file (file “file.txt"))

Files.lines(Paths.get(“file.txt")).forEach{println it};

Performance in recursion

(defn factorial ([x] (trampoline (factorial (dec x) x)))

([x a] (if (<= x 1) a #(factorial (dec x) (*' x a)))))

@TailRecursive

def fact(BigInteger n,acumulator = 1G) {

if (n <2){

acumulator

}else{

fact(n-1,n*acumulator)

}

}

@scala.annotation.tailrec

def factorialrec(fact:BigInt,number:BigInt=1):BigInt = {

if (fact<2) return number

else factorialrec(fact-1,fact*number)

}

Comparing features

Higher Order Functions

Mixin/traits

Pattern matching

Implicit casts

Nullables / Optionals

Modules

Promote inmutability

Lambdas

Palindrome

(use 'clojure.java.io)

(defn palindrome? [s]

(let [sl (.toLowerCase s)]

(= sl (apply str (reverse sl)))))

(defn find-palindromes[s]

(filter palindrome? (clojure.string/split s #" ")))

fun isPalindrome(s : String) : Boolean {

return s== s.reverse()

}

fun findPalindrome(s:List<String>): List<String>{

return s.filter {isPalindrome(it)}.map {it}

}

def isPalindrome(x:String) = x==x.reverse

def findPalindrome(s:Seq[String]) = s find isPalindrome

def isPalindrome(s) {

def s1 = s.toLowerCase()

s1 == s1.reverse()

}

def findAllPalindrome(list) {

list.findAll{isPalindrome(it)}

}

Conclusions

Full interoperability with Java Reducing Java verbosity These languages allow developer be more productive comparing with java Pragmatic Programmer advice: When you learn a new language, you learn a new way to think.

Resources

• http://vschart.com/compare/kotlin/vs/clojure/vs/scala

Thank you! José Manuel Ortega Candel | @jmortegac