3. Ziel des Vortrags
Vorstellung von Kotlin als funk onalere Java-Alterna ve
kurzer Einblick in die bekannteste Kotlin-Library für FP: Arrow
4. So leicht ist der Wechsel
einfacher Ums eg für Java-Entwickler dank hoher Java-Nähe
extrem hohe Interoperabilität zwischen Java und Kotlin
Java-zu-Kotlin-Converter in IntelliJ enthalten
problemlose Nutzung fast aller Java-Libraries möglich
6. Was bietet Kotlin?
viele unterstützte Einsatzbereiche
objektorien erte und funk onale Komponenten
prägnante Syntax (circa 40 % weniger Codezeilen als Java)
7. Variablen & Typinferenz
Variablen werden mit val (immutable) oder var (mutable) gekennzeichnet
Typen können op onal angegeben werden
val firstSpeaker = "Felicitas"
val secondSpeaker: String = "Stefan"
es gibt keine primi ven Typen (werden nur im Bytecode verwendet)
8. Null-sichere Typen
Unterscheidung zwischen nullable und non-null Referenzen
Compiler erkennt, wenn eine Variable möglicherweise null ist
fun main() {
val nonNullable: String = "Alice" // non-nullable
val nullable: String? = "Bob" // nullable
println(nonNullable.length)
println(nullable.length) // compiler error
}
9. viele Möglichkeiten zum Umgang mit nullable Referenzen (safe (?.) or non-null asserted (!!.)
calls, elvis (?:), if)
auch Chaining möglich
val streetName = order?.customer?.address?.streetName ?: ''
10. Data Classes
data class Person(val name: String, val age: Int)
fun main() {
println(Person("Sherlock", 30)) // Person(name=Sherlock, age=30)
}
Automa sche Generierung von
equals()
toString()
copy()
hashcode()
componentN() func ons (for destructuring)
12. Lambdas & Funk onstypen
Java unterstützt nur Func onal Interfaces (a.k.a. SAM)
import java.util.function.Predicate;
public class EvenChecker implements Predicate<Integer> {
public boolean test(Integer integer) {
return integer % 2 == 0;
}
public static void main(String[] args){
System.out.println(new EvenChecker().test(12));
}
}
13. Seit Java 8 können Lambdas als Implemen erung von funk onalen Interfaces verwendet werden
import java.util.function.Predicate;
class Main {
public static void main(String[] args) {
Predicate<Integer> isEven = a -> a % 2 == 0;
System.out.println(isEven.test(12));
}
}
14. Kotlin unterstützt echte Funk onstypen und behandelt Funk onen als first class ci zens
fun main() {
val isEven = { a: Int -> a % 2 == 0 }
println(isEven(12))
println(isEven) // (kotlin.Int) -> kotlin.Boolean
}
Typen können auch hier angegeben werden
fun main() {
val greet: (String) -> String = {name -> "Hello $name!"}
println(greet("JUGHH")) // Hello JUGHH!
println(greet) // (kotlin.String) -> kotlin.String
}
15. Higher Order Func ons & Par elle Funk onsanwendung
fun main() {
val greet: (getName: (Int) -> String) -> (id: Int) -> String =
{ getName -> { id -> "Hello ${getName(id)}!" } }
val getNameForId: (Int) -> String =
when(id) {
1 -> "Maria"
2 -> "Anna"
else -> "Stranger"
}
println(greet(getNameForId)) // (kotlin.Int) -> kotlin.String
println(greet(getNameForId)(1)) // Hello Maria!
}
17. Aggregate Opera ons
in Java mit Hilfe von Streams möglich
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> doubledList = list
.stream()
.map(n -> 2 - n)
.collect(Collectors.toList());
System.out.println(doubledList); // [2, 4, 6, 8, 10]
}
}
18. in Kotlin straigh orward
fun main() {
val list: List<Int> = listOf(1, 2, 3, 4, 5);
// trailing lambda
// it für single lambda parameter
val doubledList = list.map { it - 2 }
val gaussianSum = (1..100).toList().reduce(Int::plus)
}
min(), max(), average(), sum() & Versionen mit explizitem Comparator
fold(), reduce() (beide le ) & right-, indexed- und "orNull"-Versionen
19. Was fehlt Kotlin?
Sprach-Features
Higher Kinded Types
Typ-Klassen
Library-Features
Funk onen zur Komposi on und für (Un-)Currying
Funk onale Datentypen (Op on, Either etc.)
Abstrak onen (Funktoren, Monaden etc.)
24. Funk onskomposi on
Funk onskomposi on ist das Mi el um größere Programme durch Kombina on von einzelnen
Funk onen zu schreiben.
Seien A, B, C beliebige Mengen und
g: A ➝ B sowie f: B ➝ C Funk onen,
so heißt die Funk on
f ° g : A ➝ C , (f ° g)(x) = f(g(x))
Komposi on von f und g
25. Funk onskomposi on mit Arrow
Arrow bietet hierfür die Funk onen compose , andThen , forwardCompose
val parse: (String) -> Int = { it.toInt()}
val addOne: (Int) -> Int = { it + 1 }
val parseAddOne1: (String) -> Int = addOne.compose(parse)
val result = parseAddOne1("5") // = 6 ≙ addOne(parse("5"))
val parseAddOne2: (String) -> Int = parse.andThen(addOne)
val parseAddOne3: (String) -> Int = parse.forwardCompose(addOne)
26. Currying
Currying ist die Umwandlung einer Funk on mit mehreren Argumenten in eine gleichwer ge
Sequenz von Funk onen
mit jeweils einem Argument.
fun <A, B, C> ((A, B)-> C).curry() : (A) -> ((B) -> C) = {
a -> { b -> this(a, b)}
}
27. Currying mit Arrow
Arrow bietet hierfür die Funk onen curry und uncurry
val calcTax: (Double, Double) -> Double = {
value, rate -> value - rate
}
val calcTaxCurried: (Double) -> (Double) -> Double =
calcTax.curried()
val calcVat = calcTaxCurried(0.19)
val vat = calcVat(149.99)
val vat2 = calcTaxCurried.uncurried()(0.19, 149.99)
28. Par elle Funk onsanwendung
Arrow bietet hierfür die Funk onen partially2 bis partially22
val calcTaxForRate: (Double) -> Double =
calcTax.partially2(149.99);
val reducedVat = calcTaxForRate(0.07);
val Vat = calcTaxForRate(0.19);
32. Typklassen als höhere Abstrak onen
Design-Pa erns der Funk onalen Programmierung
Keine Klassen im OOP Sinne
Eher wie Interfaces auf sehr abstrakter Ebene
Besteht aus Algebra ≙ Funk onen und Gesetze ≙ Verhalten
33. Die Funk on map
//List
var l: List<Int> = listOf("1", "2", "3", "4").map { it.toInt() }
//Option
var o:Option<Int> = Some("1").map { it.toInt() }
Die Signatur von map
//List
fun <A, B> List<A>.map(f: (A) -> B): List<B>
//Option
fun <A, B> Option<A>.map(f: (A) -> B): Option<B>
35. Higher kinded Types mit Arrow
Mit dem Interface Kind lässt sich unser Problem lösen
interface Kind<out F, out A>
interface Functor<F> {
fun <A, B> Kind<F, A>.map(f: (A) -> B): Kind<F, B>
}
36. Datentyp für op onale Werte
@higherkind
sealed class Option<out A> : Kind<ForOption, A> {
companion object
}
data class Some<out A>(val get: A) : Option<A>()
object None : Option<Nothing>()
37. Arrow generiert helper
ForOption als Ersatztyp für Option<A> ohne den generischen Typ-Parameter.
Es wird sichergestellt, dass Option<A> die einzige Implemen erung von Kind<ForOption,
A> ist.
fix Funk on zur Umwandlung von Kind<ForOption, A> zu Option<A>
Option<A> zu Kind<ForOption, A> via subclassing
38. Funktor für Option
@extension
interface OptionFunctor : Functor<ForOption> {
override fun <A, B> Kind<ForOption, A>.map(f: (A) -> B): Option<B> {
val option : Option<A> = this.fix()
return when(option) {
is None -> None
is Some -> Some(f(option.get))
}
}
}
@extension :
generiert ein Objekt aus dem interface mit allen bereits definierten default-methoden
erweitert das companion object von Option um eine Funk on functor() , die das
Objekt zurückgibt
39. Anwendungsbeispiel
fun <F> abs(fa: Kind<F, Int>, FT: Functor<F>) : Kind<F, Int> =
FT.run {
fa.map { when {
it < 0 -> - it
else -> it
} }
}
val res1 = abs(LinkedList(1,-2, -3), LinkedList.functor()).fix() // [1, 2, 3]
val res2 = abs(Some(-2), Option.functor()).fix() // 2
40. Iden tät eines Funktors
Abbildung auf sich selbst ≙ unveränderter Funktor
fun <F, A> identity(fa: Kind<F, A>,
FT: Functor<F> ) : Boolean =
FT.run {
fa.map { it } == fa
}
41. Assozia vität eines Funktors
Mehrere map Funk onen nacheinander ≙ Komposi on beider map Funk onen
fun <F, A, B, C> associativity(fa: Kind<F, A>,
FT: Functor<F>, f: (A)-> B, g:(B) -> C) : Boolean =
FT.run {
fa.map(f).map(g) == fa.map(f.andThen(g))
}
42. Sind die Gesetze erfüllt?
assert(identity(Some(3), Option.functor()))
val parseInt : (String) -> Int = { s -> Integer.parseInt(s)}
val plusOne : (Int) -> Int = { x:Int -> x.plus(1)}
assert(associativity(Some("1"), Option.functor(), parseInt, plusOne))
43. Unser Eindruck
Arrow ist ein gut strukturierter Baukasten
Advanced FP ist auch mit Kotlin möglich
Simula on von Higher Kinded Types etwas gewöhnungsbedür ig
Code-Generierung macht manchmal Probleme
Im Projekt definieren, was aus dem Baukasten verwendet werden darf/soll.
44. Ich will mehr davon
Func onal Programming in Kotlin - Manning
Kotlin Koans - zum Kennenlernen von Kotlin