New Technology
Insert company logo
Lutz Hühnken / @lutzhuehnken
Scala: Durch Java 8 überflüssig geworden?
@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
Was wir nicht (mehr) wollen
List<String> getNames(List<Customer> customers) {
List<String> names = new ArrayList<String>();
for (customer : customers) {
names.add(customer.getName());
}
return names;
}
Funktionen
Scala
val names = customers.map(_.name)
Funktionen
Java 8
List<String> names =
myList.stream()
.map(c -> c.name)
.collect(Collectors.toList());
Funktionen
Javaslang
List<String> names = customers.map(c -> c.name);
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
Scala
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)
@Immutable
public final class Time {
public final Integer hours;
public final Integer minutes;
Werte public Time(Integer hours, Integer minutes) {
this.hours = hours;
this.minutes = minutes;
}
Java 8 @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();
}
}
Werte
Immutables
import org.immutables.value.Value;
@Value.Immutable
public abstract class Time {
public abstract Integer hours();
public abstract Integer minutes();
}
Werte
Immutables
Time t1 =
ImmutableTime.builder()
.hours(12)
.minutes(0)
.build();
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
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)
Datenstrukturen (Collections)
PCollections
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]
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)
Javaslang
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
• 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
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
Currying
Javaslang
Function2<Integer, Integer, Integer> sum = (a, b) -> a + b;
Function1<Integer, Integer> add2 = sum.curried().apply(2);
add2.apply(4) // 6
Tupel
Scala
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))
Tupel
Javaslang
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)
Pattern Matching
Scala
val s = i match {
case 1 => "one"
case 2 => "two"
case _ => "?"
}
Pattern Matching
Javaslang
String s = Match(i).of(
Case($(1), "one"),
Case($(2), "two"),
Case($(), "?")
);
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
Scala
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
Rekursion
Scala
def factorial(n: Int, acc: BigInt = 1): BigInt =
if (n == 0) acc else factorial(n - 1, n * acc)
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.
New Technology
Insert company logo
Lutz Hühnken / @lutzhuehnken
Scala: Durch Java 8 überflüssig geworden?
Antwort: Nein.
@lutzhuehnken 29/09/2016