Programação Funcional em C#: nível avançado
Programação Funcional (FP) é um paradigma que nos últimos anos tem penetrado ativamente no mundo do C#. Graças às capacidades do .NET, os desenvolvedores podem combinar abordagens orientadas a objetos e funcionais, criando código mais confiável, testável e previsível. Neste artigo, vamos explorar técnicas avançadas de FP em C# que o ajudarão a ir além do simples uso de LINQ.
1. Funções Puras e Efeitos Colaterais: controle rigoroso
Uma função pura é uma função cujo resultado depende apenas dos argumentos de entrada e que não causa efeitos colaterais (alteração de estado global, entrada-saída, exceções). Em C#, podemos nos aproximar desse ideal usando tipos imutáveis e evitando a modificação de parâmetros.
// Função pura: resultado depende apenas de x e ypublic static int Add(int x, int y) => x + y;
// Função impura: depende do estado externoprivate static int _counter = 0;public static int IncrementCounter() => ++_counter;Em nível avançado, é importante separar rigorosamente os cálculos puros das operações com efeitos colaterais. Para isso, pode-se usar o padrão IO (em linguagens funcionais) ou seus análogos simplificados em C# através de Func<T> e computação preguiçosa.
Técnica prática: "Execução Preguiçosa"
Em vez de executar imediatamente uma ação com efeito colateral, retorne um delegate que a executará:
public static Func<int> CreateFileReadOperation(string path){ return () => File.ReadAllText(path).Length; // Efeito colateral adiado}
// Usovar operation = CreateFileReadOperation("data.txt");// ... lógica pura ...int length = operation(); // Efeito colateral executado aqui2. Composição de Funções e Currying
Composição é a combinação de duas ou mais funções para criar uma nova. Currying é a transformação de uma função de vários argumentos em uma cadeia de funções de um argumento. Essas técnicas tornam o código mais modular e reutilizável.
Composição via LINQ e métodos próprios
Em C#, a composição pode ser implementada através de métodos de extensão:
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));}
// Exemplo: 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 em C#
Currying permite aplicar parcialmente uma função:
public static Func<int, Func<int, int>> CurriedAdd = x => y => x + y;
var addFive = CurriedAdd(5); // Aplicação parcialConsole.WriteLine(addFive(10)); // 15
// Exemplo mais complexo: função de log com prefixopublic 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 e Trabalho com Option (Maybe)
Mônada é um padrão que permite aplicar funções sequencialmente a valores encapsulados em um contexto (por exemplo, possibilidade de ausência de valor ou assincronicidade). Em C#, é popular a implementação de Option<T> ou Maybe<T> para evitar null.
Implementação simples de Option
public class Option<T>{ private readonly T _value; private readonly bool _hasValue;
pri