Programmation fonctionnelle en Kotlin : de A à Z
La programmation fonctionnelle (PF) est un paradigme qui considère les calculs comme l'évaluation de fonctions mathématiques et évite les changements d'état et les données mutables. Kotlin, en tant que langage multiparadigme, fournit des outils puissants pour appliquer l'approche fonctionnelle sans vous obliger à abandonner la POO habituelle. Dans cet article, nous allons explorer les concepts clés de la PF en Kotlin : des fonctions pures et de l'immuabilité aux techniques avancées comme la composition et les monades.
1. Fondements de l'approche fonctionnelle : fonctions pures et immuabilité
Le fondement de la PF repose sur deux principes : la pureté des fonctions et l'immuabilité des données.
- Fonction pure — c'est une fonction dont le résultat dépend uniquement des arguments passés et qui n'a pas d'effets secondaires (elle ne modifie pas les variables externes, n'affiche rien dans la console, n'accède pas à la base de données).
- Immuabilité signifie que les données ne peuvent pas être modifiées après leur création. Au lieu de modifier un objet existant, on crée une nouvelle copie avec les modifications souhaitées.
Exemple de fonction pure et impure en Kotlin :
// Fonction impure : a un effet secondaire (affichage dans la console)fun impureSum(a: Int, b: Int): Int { println("Somme : ${a + b}") // effet secondaire return a + b}
// Fonction pure : uniquement le calculfun pureSum(a: Int, b: Int): Int = a + b
// Immuabilité : utilisation de val au lieu de vardata class User(val name: String, val age: Int) // val rend les champs immuablesfun main() { val user = User("Alice", 30) // user.age = 31 // Erreur de compilation ! val olderUser = user.copy(age = 31) // création d'une nouvelle copie}L'utilisation de val et de data class avec copy() est la manière standard de respecter l'immuabilité en Kotlin.
2. Fonctions d'ordre supérieur et expressions lambda
Kotlin prend en charge les fonctions en tant qu'objets de première classe. Cela signifie que les fonctions peuvent être passées en tant qu'arguments, retournées par d'autres fonctions et stockées dans des variables. Les fonctions qui acceptent ou retournent d'autres fonctions sont appelées fonctions d'ordre supérieur.
Les expressions lambda sont un moyen compact de créer des fonctions anonymes. Syntaxe : { arguments -> corps }.
// Fonction d'ordre supérieur : prend une fonction (Int, Int) -> Intfun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b)}
fun main() { // Expressions lambda val sum: (Int, Int) -> Int = { x, y -> x + y } val multiply: (Int, Int) -> Int = { x, y -> x * y }
println(operateOnNumbers(5, 3, sum)) // 8 println(operateOnNumbers(5, 3, multiply)) // 15
// Utilisation d'une lambda "à la volée" println(operateOnNumbers(10, 2, { a, b -> a - b })) // 8
// Si la lambda est le dernier argument, on peut la sortir des parenthèses (trailing lambda) println(operateOnNumbers(10, 2) { a, b -> a / b }) // 5}La bibliothèque standard de Kotlin utilise activement les fonctions d'ordre supérieur : map, filter, reduce pour les collections.
val numbers = listOf(1, 2, 3, 4, 5)val evenSquares = numbers .filter { it % 2 == 0 } // {2, 4} .map { it * it } // {4, 16}println(evenSquares) // [4, 16]3. Composition de fonctions et curryfication
La composition est le processus qui consiste à combiner deux ou plusieurs fonctions pour en créer une nouvelle. En Kotlin, il n'y a pas d'opérateur de composition intégré comme en Haskell, mais il est facile de l'implémenter manuellement ou à l'aide de fonctions d'extension.
// Composition de deux fonctionsinfix fun ((B) -> C).compose(f: (A) -> B): (A) -> C = { a -> this(f(a)) }
fun main() { val toInt: (String) -> Int = { it.toInt() } val square: (Int) -> Int = { it * it }
val squ