Programmation fonctionnelle en Kotlin : niveau avancé

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Programmation fonctionnelle en Kotlin : niveau avancé

Kotlin est un langage multi-paradigme qui combine harmonieusement les approches orientée objet et fonctionnelle. Si vous maîtrisez déjà les concepts de base de la programmation fonctionnelle (PF), comme map, filter, fold et les lambdas, il est temps de plonger dans les techniques avancées. Dans cet article, nous aborderons la composition de fonctions, le currying, l'évaluation paresseuse, ainsi que les foncteurs et les monades avec Kotlin. Tous les exemples de code peuvent être exécutés dans votre IDE.



1. Composition de fonctions et currying

La composition consiste à créer une nouvelle fonction en appliquant successivement deux ou plusieurs fonctions. Kotlin n'a pas d'opérateur de composition intégré comme Haskell, mais il peut être facilement implémenté via des fonctions d'extension.



Exemple de composition de fonctions

// Définir les fonctionsval double: (Int) -> Int = { it * 2 }val increment: (Int) -> Int = { it + 1 }

// Composition : d'abord double, puis 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

Le currying est la transformation d'une fonction à plusieurs arguments en une chaîne de fonctions, chacune prenant un seul argument. En Kotlin, cela se fait avec des lambdas imbriquées.

// Fonction normalefun sum(a: Int, b: Int): Int = a + b

// Version curryfiéefun curriedSum(a: Int): (Int) -> Int = { b -> a + b }

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

// Fonction universelle pour le 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

Le currying est particulièrement utile pour l'application partielle et les fabriques de fonctions.



2. Foncteurs et monades : théorie en pratique

Un foncteur est un conteneur qui peut être transformé (map) à l'aide d'une fonction. Une monade est un foncteur avec la capacité de composer des conteneurs (flatMap). En Kotlin, des exemples frappants de monade sont Result (ou Either), ainsi que Sequence.



Exemple avec Result (monade Either)

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

fun parseUser(json: String): Result<User> = runCatching { // Simulation d'analyse 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")}

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

println(result.getOrDefault("Failed"))

Ici, flatMap permet d'éviter les vérifications imbriquées de succès/échec, rendant le code linéaire et lisible.



Créer son propre foncteur : le type 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

Book Recommendations