41
Insert company logo New Technology Scala: Durch Java 8 überflüssig geworden? Lutz Hühnken / @lutzhuehnken @lutzhuehnken 29/09/2016

Funktionale Programmierung in Java 8 und Scala auf der CodeTalks Hamburg am am 29.9.2016

Embed Size (px)

Citation preview

Insert company logo

New Technology

Scala: Durch Java 8 überflüssig geworden?Lutz Hühnken / @lutzhuehnken

@lutzhuehnken 29/09/2016

Was macht funktionale Programmierung aus?

• Funktionen • (Unveränderliche) Werte • Keine Seiteneffekte

Funktionen

• Code vs. Daten

scala> val sum = (x:Int, y:Int) => x + y Type of sum is (Int, Int) => Int

Werte

• Wert vs. Identität • kein PlOP

Integer x = new Integer(5)

String hello = „Hello“

Keine Seiteneffekte

• Ausdrücke evaluieren vs. Anweisungen ausführen • Ziel: Referenzielle Transparenz

Funktionale Programmierung Scala vs. Java8

Die Disziplinen

• Funktionen höherer Ordnung • Unveränderliche Werte und Datenstrukturen • Currying, partiell evaluierte Funktionen • Tupel • Pattern Matching • Ausdrücke • Rekursion

Die Teams

• Scala - http://www.scala-lang.org • Vereinigt OO mit FP

• Java8 mit Verstärkung: • Immutables - https://immutables.github.io • PCollections - http://pcollections.org • Javaslang - http://www.javaslang.io • Halva - https://github.com/soabase/soabase-halva

Funktionen

List<String> getNames(List<Customer> customers) { List<String> names = new ArrayList<String>(); for (customer : customers) { names.add(customer.getName()); } return names; }

Was wir nicht (mehr) wollen

Funktionen

val names = customers.map(_.name)

Scala

Funktionen

List<String> names = myList.stream()

.map(c -> c.name)

.collect(Collectors.toList());

Java 8

Funktionen

List<String> names = customers.map(c -> c.name);

Javaslang

Unveränderliche Werte

• Werte sind unveränderlich - nicht nur die Referenz (final), auch das Object, auf das sie zeigt! Werte

• Können geteilt werden • Haben stabilen Hashcode • Können übertragen werden

Werte

case class Time(hours: Int = 0, minutes: Int = 0)

val t1 = Time(12,0)

val t2 = Time(hours = 12)

val t3 = t2.copy(minutes = 30)

Scala

Werte

@Immutable public final class Time {

public final Integer hours; public final Integer minutes;

public Time(Integer hours, Integer minutes) { this.hours = hours; this.minutes = minutes; }

@Override public boolean equals(@Nullable Object other) { if (this == other) return true; if (!(other instanceof Time)) return false; Time otherTime = (Time) other;

return hours.equals(otherTime.hours) && minutes.equals(otherTime.minutes); }

@Override public int hashCode() { return Objects.hash(hours, minutes); }

@Override public String toString() { return MoreObjects.toStringHelper("Time") .add("hours", hours).add("minutes", minutes).toString(); } }

Java 8

Werte

import org.immutables.value.Value;

@Value.Immutable public abstract class Time { public abstract Integer hours(); public abstract Integer minutes(); }

Immutables

Werte

Time t1 = ImmutableTime.builder() .hours(12) .minutes(0) .build();

Immutables

Werte

Halva

@CaseClass public interface Time { Integer hours(); Integer minutes(); }

Time t1 = new TimeCase(12,0);

Unveränderliche Datenstrukturen

Für Aggregate (Collections) muss das gleiche gelten wie für ihre Elemente!

Auch sie wollen wir teilen und übertragen können - kein „PlOP“.

Datenstrukturen (Collections)

scala> val v1 = Vector(1,2,3) v1: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)

scala> v1.updated(0,3) res0: scala.collection.immutable.Vector[Int] = Vector(3, 2, 3)

scala> v1 res1: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)

Scala

Datenstrukturen (Collections)

import org.pcollections.PVector;import org.pcollections.TreePVector;

PVector<Integer> pv1 = TreePVector.from(Arrays.asList(1,2,3));

pv1.with(0,3);

System.out.println(pv1.toString()); // [1, 2, 3]

PCollections

Collections

• Fokussiert auf persistente Collections • Minimale API (plus, minus, with..) • Keine zusätzlichen Methoden, keine Lambda-

Unterstützung • Drop-In-Replacement (PVector implementiert

java.util.List, HashPMap impl. java.util.Map..)

Datenstrukturen (Collections)

import javaslang.collection.Vector;

Vector<Integer> v1 = Vector.of(1,2,3);

v1.update(0,3);

System.out.println(v1.mkString(",")); // (1,2,3)

Javaslang

Javaslang

• Bietet „reiche“ API (mit drop, take, permutation, map, flatMap, filter, collect, sliding, etc., entsprechend Scala-Collections.), inkl. Funktionen höherer Ordnung.

• Kein Drop-In-Replacement (implementiert nicht java.util.Collection etc.)

Currying

scala> def sum(a:Int)(b:Int) = a + b sum: (a: Int)(b: Int)Int

scala> val sum2: Function[Int,Int] = sum(2) sum2: Function[Int,Int] = <function1>

scala> sum2(4) res1: Int = 6

Scala

Currying

Function2<Integer, Integer, Integer> sum = (a, b) -> a + b;

Function1<Integer, Integer> add2 = sum.curried().apply(2);

add2.apply(4) // 6

Javaslang

Tupel

scala> val java8 = ("Java", 8) java8: (String, Int) = (Java,8)

scala> java8._1 res0: String = Java

scala> java8._2 res1: Int = 8

scala> List("a","b","c").zip(List(1,2,3)) res2: List[(String, Int)] = List((a,1), (b,2), (c,3))

Scala

Tupel

Tuple2<String, Integer> java8 = Tuple.of("Java", 8);

String s = java8._1; // "Java"

Integer i = java8._2; // 8

List<Tuple2<String,Integer>> zipped = List.of("a", "b", "c").zip(List.of(1,2,3));System.out.println(zipped.mkString(", ")); // (a, 1), (b, 2), (c, 3)

Javaslang

Pattern Matching

val s = i match { case 1 => "one" case 2 => "two" case _ => "?" }

Scala

Pattern Matching

String s = Match(i).of( Case($(1), "one"), Case($(2), "two"), Case($(), "?") );

Javaslang

Ausdrücke

• Ausdrücke („Expressions“) vs. Befehle („Statements“). • Statements haben kein Ergebnis und werden nur um

des Seiteneffekts wegen ausgeführt. • Wichtige Java-Kontrollstrukturen sind Statements (if,

for, while, try…). • In Scala ist alles ein Ausdruck.

Ausdrücke

for { n <- 1 to 3 m <- 1 to n } yield n * m

res0: Vector(1, 2, 4, 3, 6, 9)

scala> val b = if (3 < 4) "yes" else "no" b: String = yes

Scala

Rekursion

def factorial(n: Int, acc: BigInt = 1): BigInt = if (n == 0) acc else factorial(n - 1, n * acc)

Scala

Rekursion

• In Scala ok, da rechtsrekursiv (tail recursive) • In Java - java.lang.StackOverflowError

Problemfelder in Java

• Syntax - Unterstützung für Datenklassen (kommt in Java 10?), Tupel fehlt

• Unveränderliche Datenstrukturen (Collections) fehlen • Zu viele Anweisungen (if, switch, for..), zu wenig

Ausdrücke • Checked Exceptions • Keine Tail-Call-Optimierung für rechtsrekursive

Funktionen

Fazit

• Mit der Kombination Lambdas, Javaslang und Halva oder Immutables lässt sich brauchbar funktional programmieren - es gibt also keine Ausrede!

• Es gibt aber noch viel zu tun! Scala bleibt fürs erste die deutlich komfortablere Lösung.

Insert company logo

New Technology

Scala: Durch Java 8 überflüssig geworden?Antwort: Nein.

Lutz Hühnken / @lutzhuehnken

@lutzhuehnken 29/09/2016