Programmation fonctionnelle en Go : niveau avancé
\n\nGo (Golang) n'est pas un langage purement fonctionnel, mais son puissant système de types, sa prise en charge des fermetures et des fonctions de première classe permettent d'appliquer efficacement les paradigmes fonctionnels. Au niveau avancé, la programmation fonctionnelle en Go aide à écrire un code plus prévisible, testable et sûr pour la concurrence. Dans cet article, nous examinerons les concepts clés : fonctions pures, composition, fermetures, curryfication, patrons monadiques et gestion des erreurs sans exceptions.
\n\n1. Fonctions pures et immutabilité
\n\nUne fonction pure est une fonction qui :
\n- \n
- Retourne toujours le même résultat pour les mêmes arguments (déterminisme). \n
- N'a pas d'effets secondaires (ne modifie pas l'état externe, n'écrit pas dans des fichiers/réseau). \n
En Go, l'immutabilité est obtenue en passant des copies de données et en utilisant des constantes. Exemple de fonction pure :
\n\npackage main\n\nimport "fmt"\n\n// Fonction pure : ne modifie pas la tranche d'origine, en retourne une nouvelle\nfunc DoubleSlice(s []int) []int {\n result := make([]int, len(s))\n for i, v := range s {\n result[i] = v * 2\n }\n return result\n}\n\nfunc main() {\n original := []int{1, 2, 3}\n doubled := DoubleSlice(original)\n fmt.Println(original) // [1 2 3] — inchangé\n fmt.Println(doubled) // [2 4 6]\n}\n\nL'utilisation de fonctions pures simplifie le refactoring et les tests, car il n'est pas nécessaire de prendre en compte un état caché.
\n\n2. Fonctions d'ordre supérieur et fermetures
\n\nEn Go, les fonctions sont des objets de première classe : elles peuvent être passées comme arguments, retournées par d'autres fonctions et stockées dans des variables. Une fermeture est une fonction qui capture des variables de la portée externe.
\n\nExemple de fonction d'ordre supérieur — Filter :
package main\n\nimport "fmt"\n\n// Filter retourne une nouvelle tranche contenant les éléments satisfaisant le prédicat\nfunc Filter[T any](s []T, predicate func(T) bool) []T {\n var result []T\n for _, v := range s {\n if predicate(v) {\n result = append(result, v)\n }\n }\n return result\n}\n\nfunc main() {\n numbers := []int{1, 2, 3, 4, 5, 6}\n even := Filter(numbers, func(n int) bool { return n%2 == 0 })\n fmt.Println(even) // [2 4 6]\n}\n\nLes fermetures sont souvent utilisées pour créer des fabriques de fonctions ou pour encapsuler un état :
\n\npackage main\n\nimport "fmt"\n\n// Counter crée une fermeture qui stocke un état interne\nfunc Counter() func() int {\n count := 0\n return func() int {\n count++\n return count\n }\n}\n\nfunc main() {\n c1 := Counter()\n fmt.Println(c1()) // 1\n fmt.Println(c1()) // 2\n\n c2 := Counter()\n fmt.Println(c2()) // 1 — état indépendant\n}\n\n3. Composition de fonctions et application partielle (curryfication)
\n\nLa composition est la combinaison de plusieurs fonctions en une seule nouvelle fonction. En Go, la composition est implémentée via des fonctions intermédiaires. L'application partielle (curryfication) permet de fixer une partie des arguments et de retourner une fonction attendant les arguments restants.
\n\nExemple de composition :
\n\npackage main\n\nimport "fmt"\n\n// Compose combine deux fonctions : f(g(x))\nfunc Compose[A, B, C any](f func(B) C, g func(A) B) func(A) C {\n return func(x A) C {\n return f(g(x))\n }\n}\n\nfunc addOne(x int) int { return x + 1 }\nfunc double(x int) int { return x * 2 }\n\nfunc main() {\n addThenDouble := Compose(double, addOne)\n fmt.Println(addThenDouble(3)) // (3+1)*2 = 8\n}\n\nCurryfication avec l'exemple d'une fonction de somme :
\n\npackage main\n\nimport "fmt"\n\n// Add — version curryfiée de l'addition\nfunc Add(a int) func(int) int {\n return func(b int) int {\n return a + b\n }\n}\n\nfunc main() {\n addFive := Add(5)\n fmt.