Funktionale Programmierung in Kotlin: Fortgeschrittenes Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Funktionale Programmierung in Kotlin: Fortgeschrittenes Niveau

Kotlin ist eine multiparadigmatische Sprache, die objektorientierte und funktionale Ansätze nahtlos verbindet. Wenn Sie bereits die grundlegenden Konzepte der funktionalen Programmierung (FP) wie map, filter, fold und Lambdas beherrschen, ist es Zeit, in fortgeschrittene Techniken einzutauchen. In diesem Artikel behandeln wir Funktionskomposition, Currying, faule Berechnungen und lernen Funktoren und Monaden am Beispiel von Kotlin kennen. Alle Codebeispiele können in Ihrer IDE ausgeführt werden.



1. Funktionskomposition und Currying

Komposition ist die Erstellung einer neuen Funktion durch die sequentielle Anwendung von zwei oder mehr Funktionen. Kotlin hat keinen eingebauten Kompositionsoperator wie Haskell, aber er kann leicht durch Extension-Funktionen implementiert werden.



Beispiel für Funktionskomposition

// Funktionen definierenval double: (Int) -> Int = { it * 2 }val increment: (Int) -> Int = { it + 1 }

// Komposition: zuerst double, dann incrementinfix fun <A, B, C> ((B) -> C).compose(f: (A) -> B): (A) -> C = { a -> this(f(a)) }

val doubleThenIncrement = increment compose doubleprintln(doubleThenIncrement(5)) // 11 (5*2 + 1)


Currying

Currying ist die Umwandlung einer Funktion mit mehreren Argumenten in eine Kette von Funktionen, die jeweils ein Argument nehmen. In Kotlin wird dies mit verschachtelten Lambdas implementiert.

// Normale Funktionfun sum(a: Int, b: Int): Int = a + b

// Curry-Versionfun curriedSum(a: Int): (Int) -> Int = { b -> a + b }

val add5 = curriedSum(5)println(add5(3)) // 8

// Universelle Funktion zum Curryingfun <A, B, C> ((A, B) -> C).curry(): (A) -> (B) -> C = { a -> { b -> this(a, b) } }

val curriedSum2 = ::sum.curry()println(curriedSum2(10)(20)) // 30

Currying ist besonders nützlich bei der Arbeit mit partieller Anwendung und Funktionsfabriken.



2. Funktoren und Monaden: Theorie in der Praxis

Ein Funktor ist ein Container, der mit einer Funktion abgebildet (map) werden kann. Eine Monade ist ein Funktor mit der Möglichkeit der Container-Komposition (flatMap). In Kotlin sind Result (oder Either) sowie Sequence prominente Beispiele für Monaden.



Beispiel mit Result (Monade Either)

data class User(val name: String, val email: String)

fun parseUser(json: String): Result<User> = runCatching { // Parsing simulieren if (json.isBlank()) throw IllegalArgumentException("Empty JSON") User("Alice", "alice@example.com")}

fun sendEmail(user: User): Result<String> = runCatching { if (user.email.contains("@")) "Email sent to ${user.email}" else throw IllegalArgumentException("Invalid email")}

// Monadische Ketteval result = parseUser("{...}") .flatMap { user -> sendEmail(user) } .map { message -> "Success: $message" } .onFailure { error -> println("Error: ${error.message}") }

println(result.getOrDefault("Failed"))

Hier vermeidet flatMap verschachtelte Erfolgs-/Fehlerprüfungen, was den Code linear und lesbar macht.



Eigenen Funktor erstellen: Typ Option

sealed class Option<out T> {    data class Some<T>(val value: T) : Option<T>()    object None : Option<Nothing>()}

fun <T, R> Option<T>.map(f: (T) -> R): Option<R> = when (this) { is Option.Some -> Option.Some(f(value)) is Option.None -> Option.None}

fun <T, R> Option<T>.flatMap(f: (T) -> Option<R>): Option<R> = when (this) { is Option.Some -> f(value) is Option.None -> Option.None}

val maybeNumber: Option<Int> = Option.Some(42)val resultOption = maybeNumber .map { it * 2 } .flatMap { Option.Some("Value: $it") }

println(resultOption) // Some(value=Value: 84)

Blogs

Buchempfehlungen