Frank Thomas und Benjamin Trenker
Eine Funktion A => B bildet jeden Wert vom Typ A auf genau einen Wert vom Typ B ab.
Und mehr nicht.
An expression e is referentially transparent if, for all programs p, all occurrences of e in p can be replaced by the result of evaluating e without affecting the meaning of p. A function f is pure if the expression f(x) is referentially transparent for all referentially transparent x
Ist StringBuilder#append referentiell transparent?
x = new StringBuilder("Hello")
r1 = x.append(" World")
r2 = x.append(" World")
=> nicht referenziell transparent
r1 == "Hello World"
r2 == "Hello World World"
Ist "hello(...)" referenziell transparent?
def hello(name: String) = new StringBuilder("Hello ")
.append(name)
.append("!")
.toString
=> ja, da von außen beobachtet kein Effekt auftritt
=> der Parameter vom Typ String ist ebenfalls referenziell transparent
Option[T]
und Either[A, B]
Beinhaltet einen Wert vom Typ T
oder nicht.
Wie eine Liste mit maximal einem Element.
Besser als null
, weil
Option[T]
nicht als T
unterjubeln können val m1: java.util.Map[String, Integer] = ...
val m2: java.util.Map[String, Integer] = ...
val m3: java.util.Map[String, Integer] = ...
val key = "two"
var result: Integer = null
val v1 = m1.get(key)
if (v1 != null) {
val v2 = m2.get(key)
if (v2 != null) {
val v3 = m3.get(key)
if (v3 != null) {
result = v1 + v2 + v3
}
}
}
val m1: Map[String, Int] = ...
val m2: Map[String, Int] = ...
val m3: Map[String, Int] = ...
val key = "two"
val result: Option[Int] = for {
v1 <- m1.get(key)
v2 <- m2.get(key)
v3 <- m3.get(key)
} yield v1 + v2 + v3
val x: Option[Int] = ...
val res: Int = x.map(i => i + 2)
.filter(i => i < 10)
.getOrElse(42)
An geeigneter Stelle kann entschieden werden wie mit einem nicht vorhandenen Wert umgegangen werden soll.
Ist entweder ein A
oder ein B
.
Vorteil gegenüber Option
: Fehlerursache kann zurückgegeben werden
def safeToInt(s: String): Either[NumberFormatError, Int] = ...
safeToInt("120").right.map(i => i + 1)
Es gibt noch mehr Datentypen, um mit Fehlern umzugehen.
Idee: Datentyp einführen, der den Kontext Zufälligkeit repräsentiert
Rng[T]
Analog zu Option[T]
: Kontext ist mögliche Fehlen eines Wertes
Rng[Double]
repräsentiert einen zufälligen Double
val d1: Rng[Double] = Rng.double // und
val d2: Rng[Double] = Rng.double // sind gleichbedeutend
Rng.seetseed(7).flatMap(_ => d1).run.unsafePerformIO()
// 0.7306990420600421
Rng.seetseed(7).flatMap(_ => d2).run.unsafePerformIO()
// 0.7306990420600421
Rng
- Slides: http://fthomas.github.io/fp-talk
- Code: https://github.com/fthomas/fp-talk
- Functional Programming in Scala
- IO
: https://github.com/scalaz/scalaz
- Rng
: https://github.com/NICTA/rng