Introduction à la programmation fonctionnelle en Kotlin
Kotlin est un langage moderne multi-paradigme qui combine harmonieusement la programmation orientée objet et fonctionnelle. Grâce à sa syntaxe concise et à ses puissantes capacités, Kotlin permet d'écrire un code propre, prévisible et facile à tester. La programmation fonctionnelle (PF) en Kotlin n'est pas simplement une tendance, mais un outil puissant pour résoudre des problèmes quotidiens : du traitement de collections à la construction de chaînes d'appels asynchrones.
Dans cet article, nous allons décomposer les concepts clés de la PF appliqués à Kotlin : l'immutabilité des données, les fonctions en tant que valeurs de première classe, les expressions lambda, les fonctions d'ordre supérieur et les chaînes de traitement de collections (map, filter, reduce). Tous les exemples seront accompagnés de code pratique que vous pouvez exécuter immédiatement dans IntelliJ IDEA ou un compilateur en ligne.
Fondamentaux de l'approche fonctionnelle en Kotlin
Immutabilité (Immutability)
Le principe fondamental de la PF est de travailler avec des données immuables. En Kotlin, on utilise pour cela les mots-clés val (référence immuable) et les collections immuables (listOf, mapOf, setOf).
// Liste immuableval numbers: List<Int> = listOf(1, 2, 3, 4, 5)
// Tenter de la modifier provoquera une erreur de compilation// numbers.add(6) // Erreur !
// Au lieu de muter, on crée une nouvelle collectionval newNumbers = numbers + 6 // [1, 2, 3, 4, 5, 6]Les avantages de l'immutabilité sont évidents : le code devient thread-safe, plus facile à déboguer, et les effets de bord sont minimisés. Si vous devez modifier des données, vous créez toujours une nouvelle copie avec les changements souhaités.
Fonctions en tant que valeurs de première classe
En Kotlin, les fonctions sont des objets à part entière. On peut les assigner à des variables, les passer à d'autres fonctions et les retourner depuis des fonctions. Le type d'une fonction s'écrit entre parenthèses avec une flèche :
// Fonction comme valeurval sum: (Int, Int) -> Int = { a, b -> a + b }
// Utilisationval result = sum(10, 20) // 30
// Fonction qui prend une autre fonction en paramètrefun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b)}
println(operate(5, 3, sum)) // 8println(operate(5, 3, { x, y -> x * y })) // 15Notez bien : si le lambda est le dernier argument, Kotlin permet de le placer en dehors des parenthèses — c'est la syntaxe trailing lambda, qui rend le code très lisible.
Expressions lambda et fonctions d'ordre supérieur
Expressions lambda
Un lambda est une fonction anonyme, encadrée par des accolades. En Kotlin, les lambdas peuvent capturer des variables du contexte externe (fermetures / closures).
// Lambda simpleval greet = { name: String -> "Bonjour, $name!" }println(greet("Anna")) // Bonjour, Anna!
// Lambda avec plusieurs paramètresval multiply: (Int, Int) -> Int = { a, b -> a * b }
// S'il n'y a qu'un seul paramètre, on peut utiliser itval double = { it: Int -> it * 2 }println(double(5)) // 10Fonctions d'ordre supérieur (Higher-Order Functions)
Ce sont des fonctions qui acceptent d'autres fonctions comme arguments ou qui en retournent. La bibliothèque standard de Kotlin en contient de nombreuses : let, apply, run, with et d'autres. Examinons un exemple pratique :
data class User(val name: String, val age: Int)
fun processUsers(users: List<User>, filter: (User) -> Boolean, transform: (User) -> String): List<String> { return users.filter(filter).map(transform)}
val users = listOf( User("Ivan", 25), User("Maria", 17), User("Pierre", 30))
// Filtrons les majeurs et affichons leurs nomsval adultNames = processUsers( users, filter = { it.age >= 18 }, transform = { "Adulte : ${it.name}" })