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 - [Link]
• Vereinigt OO mit FP
• Java8 mit Verstärkung:
• Immutables - [Link]
• PCollections - [Link]
• Javaslang - [Link]
• Halva - [Link]
Funktionen
Was wir nicht (mehr) wollen
List<String> getNames(List<Customer> customers) {
List<String> names = new ArrayList<String>();
for (customer : customers) {
[Link]([Link]());
}
return names;
}
Funktionen
Scala
val names = [Link](_.name)
Funktionen
Java 8
List<String> names =
[Link]()
.map(c -> [Link])
.collect([Link]());
Funktionen
Javaslang
List<String> names = [Link](c -> [Link]);
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 = [Link](minutes = 30)
@Immutable
public final class Time {
public final Integer hours;
public final Integer minutes;
Werte public Time(Integer hours, Integer minutes) {
[Link] = hours;
[Link] = 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
[Link]([Link]) &&
[Link]([Link]);
}
@Override
public int hashCode() {
return [Link](hours, minutes);
}
@Override
public String toString() {
return [Link]("Time")
.add("hours", hours).add("minutes", minutes).toString();
}
}
Werte
Immutables
import [Link];
@[Link]
public abstract class Time {
public abstract Integer hours();
public abstract Integer minutes();
}
Werte
Immutables
Time t1 =
[Link]()
.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: [Link][Int] = Vector(1, 2, 3)
scala> [Link](0,3)
res0: [Link][Int] = Vector(3, 2, 3)
scala> v1
res1: [Link][Int] = Vector(1, 2, 3)
Datenstrukturen (Collections)
PCollections
import [Link];
import [Link];
PVector<Integer> pv1 = [Link]([Link](1,2,3));
[Link](0,3);
[Link]([Link]()); // [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
[Link], HashPMap impl. [Link]..)
Datenstrukturen (Collections)
Javaslang
import [Link];
Vector<Integer> v1 = [Link](1,2,3);
[Link](0,3);
[Link]([Link](",")); // (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
[Link] 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 = [Link]().apply(2);
[Link](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 = [Link]("Java", 8);
String s = java8._1; // "Java"
Integer i = java8._2; // 8
List<Tuple2<String,Integer>> zipped = [Link]("a", "b", "c").zip([Link](1,2,3));
[Link]([Link](", ")); // (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 - [Link]
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