Programación funcional en C#: nivel avanzado
La programación funcional (FP) es un paradigma que en los últimos años ha penetrado activamente en el mundo de C#. Gracias a las capacidades de .NET, los desarrolladores pueden combinar enfoques orientados a objetos y funcionales, creando código más confiable, testeable y predecible. En este artículo analizaremos técnicas avanzadas de FP en C# que te ayudarán a ir más allá del simple uso de LINQ.
1. Funciones puras y efectos secundarios: control estricto
Una función pura es una función cuyo resultado depende únicamente de los argumentos de entrada y que no causa efectos secundarios (cambio de estado global, entrada/salida, excepciones). En C# podemos acercarnos a este ideal usando tipos inmutables y evitando la modificación de parámetros.
// Función pura: el resultado depende solo de x y ypublic static int Add(int x, int y) => x + y;
// Función impura: depende del estado externoprivate static int _counter = 0;public static int IncrementCounter() => ++_counter;A nivel avanzado es importante separar estrictamente los cálculos puros de las operaciones con efectos secundarios. Para esto se puede usar el patrón IO (en lenguajes funcionales) o sus análogos simplificados en C# mediante Func<T> y evaluaciones perezosas.
Técnica práctica: «Ejecución diferida»
En lugar de ejecutar inmediatamente una acción con efecto secundario, devuelve un delegado que la ejecutará:
public static Func<int> CreateFileReadOperation(string path){ return () => File.ReadAllText(path).Length; // Efecto secundario diferido}
// Usovar operation = CreateFileReadOperation("data.txt");// ... lógica pura ...int length = operation(); // El efecto secundario se ejecuta aquí2. Composición de funciones y currying
La composición es la combinación de dos o más funciones para crear una nueva. El currying es la transformación de una función de varios argumentos en una cadena de funciones de un solo argumento. Estas técnicas hacen que el código sea más modular y reutilizable.
Composición mediante LINQ y métodos propios
En C# la composición se puede implementar mediante métodos de extensión:
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));}
// Ejemplo: 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)); // 17Currying en C#
El currying permite aplicar parcialmente una función:
public static Func<int, Func<int, int>> CurriedAdd = x => y => x + y;
var addFive = CurriedAdd(5); // Aplicación parcialConsole.WriteLine(addFive(10)); // 15
// Ejemplo más complejo: función de logging con prefijopublic static Func<string, Func<string, string>> CreateLogger = prefix => message => $"[{prefix}] {message}";
var infoLogger = CreateLogger("INFO");Console.WriteLine(infoLogger("Sistema iniciado")); // [INFO] Sistema iniciado3. Mónadas y trabajo con Option (Maybe)
Una mónada es un patrón que permite aplicar funciones secuencialmente a valores envueltos en un contexto (por ejemplo, posibilidad de ausencia de valor o asincronía). En C# es popular la implementación de Option<T> o Maybe<T> para evitar null.
Implementación simple de Option
public class Option<T>{ private readonly T _value; private readonly bool _hasValue; pri