Programmation fonctionnelle en C# : niveau avancé
La programmation fonctionnelle (PF) est un paradigme qui, ces dernières années, pénètre activement le monde du C#. Grâce aux capacités de .NET, les développeurs peuvent combiner les approches orientée objet et fonctionnelle, créant ainsi un code plus fiable, testable et prévisible. Dans cet article, nous allons examiner les techniques avancées de PF en C# qui vous aideront à dépasser la simple utilisation de LINQ.
1. Fonctions pures et effets de bord : un contrôle strict
Une fonction pure est une fonction dont le résultat dépend uniquement des arguments d'entrée et qui ne provoque pas d'effets de bord (modification de l'état global, entrées-sorties, exceptions). En C#, nous pouvons nous rapprocher de cet idéal en utilisant des types immuables et en évitant la modification des paramètres.
// Fonction pure : le résultat dépend uniquement de x et ypublic static int Add(int x, int y) => x + y;
// Fonction impure : dépend de l'état externeprivate static int _counter = 0;public static int IncrementCounter() => ++_counter;À un niveau avancé, il est important de séparer strictement les calculs purs des opérations avec effets de bord. Pour cela, on peut utiliser le modèle IO (dans les langages fonctionnels) ou ses analogues simplifiés en C# via Func<T> et les calculs différés.
Astuce pratique : « Exécution différée »
Au lieu d'effectuer immédiatement une action avec effet de bord, retournez un délégué qui l'exécutera :
public static Func<int> CreateFileReadOperation(string path){ return () => File.ReadAllText(path).Length; // Effet de bord différé}
// Utilisationvar operation = CreateFileReadOperation("data.txt");// ... logique pure ...int length = operation(); // L'effet de bord est exécuté ici2. Composition de fonctions et curryfication
La composition est l'union de deux ou plusieurs fonctions pour en créer une nouvelle. La curryfication est la transformation d'une fonction à plusieurs arguments en une chaîne de fonctions à un seul argument. Ces techniques rendent le code plus modulaire et réutilisable.
Composition via LINQ et méthodes personnalisées
En C#, la composition peut être implémentée via des méthodes d'extension :
public static class FuncExtensions{ public static Func<T, TResult> Compose<T, TIntermediate, TResult>( this Func<T, TIntermediate> first, Func<TIntermediate, TResult> second) => x => second(first(x));}
// Exemple : f(x) = x + 2, g(x) = x * 3, h = f ∘ gFunc<int, int> addTwo = x => x + 2;Func<int, int> multiplyByThree = x => x * 3;var composed = multiplyByThree.Compose(addTwo); // h(x) = (x*3) + 2Console.WriteLine(composed(5)); // 17Curryfication en C#
La curryfication permet d'appliquer partiellement une fonction :
public static Func<int, Func<int, int>> CurriedAdd = x => y => x + y;
var addFive = CurriedAdd(5); // Application partielleConsole.WriteLine(addFive(10)); // 15
// Exemple plus complexe : fonction de journalisation avec préfixepublic static Func<string, Func<string, string>> CreateLogger = prefix => message => $"[{prefix}] {message}";
var infoLogger = CreateLogger("INFO");Console.WriteLine(infoLogger("Système démarré")); // [INFO] Système démarré3. Monades et travail avec Option (Maybe)
Une monade est un modèle qui permet d'appliquer séquentiellement des fonctions à des valeurs encapsulées dans un contexte (par exemple, la possibilité d'absence de valeur ou l'asynchronisme). En C#, l'implémentation de Option<T> ou Maybe<T> est populaire pour éviter les null.
Implémentation simple d'Option
public class Option<T>{ private readonly T _value; private readonly bool _hasValue;
pri