Programação Funcional em C#: da Teoria à Prática
Programação Funcional (PF) é um paradigma onde os cálculos são tratados como avaliação de funções matemáticas, evitando mudanças de estado e dados mutáveis. Embora C# tenha sido originalmente uma linguagem orientada a objetos, a partir da versão 3.0 ela vem absorvendo ativamente recursos funcionais. Hoje, C# suporta conceitos como expressões lambda, LINQ, tipos imutáveis e até pattern matching. Neste artigo, vamos analisar os princípios-chave da PF e mostrar como aplicá-los em projetos reais com C#.
1. Funções Puras e Ausência de Efeitos Colaterais
Função pura é uma função que:
- Sempre retorna o mesmo resultado para os mesmos argumentos (transparência referencial).
- Não possui efeitos colaterais (não altera estado global, não escreve em arquivos, não envia requisições HTTP).
Em C#, uma função pura pode ser assim:
public static int Add(int a, int b) => a + b;Já um exemplo de função impura:
public static int AddAndLog(int a, int b){ Console.WriteLine($"Somando {a} e {b}"); // efeito colateral return a + b;}Vantagens das funções puras: são fáceis de testar, depurar e paralelizar.
2. Imutabilidade (Immutability)
Na PF, os dados são imutáveis por padrão. Em C#, usamos para isso:
- A palavra-chave
readonlypara campos. - Structs
record(C# 9+), que criam automaticamente objetos imutáveis. - Coleções do namespace
System.Collections.Immutable.
Exemplo com record:
public record Person(string Name, int Age);Se precisar alterar a idade, uma nova instância é criada:
Person person = new("Ivan", 30);Person olderPerson = person with { Age = 31 };A imutabilidade previne mutações acidentais e simplifica a concorrência.
3. Funções de Alta Ordem e Expressões Lambda
Funções de alta ordem recebem outras funções como argumentos ou as retornam. Em C#, isso é implementado através de delegates: Func<>, Action<>, Predicate<>.
Um exemplo simples é o método Where do LINQ:
List<int> numbers = new() { 1, 2, 3, 4, 5 };var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();Aqui, n => n % 2 == 0 é uma expressão lambda (função anônima).
Podemos escrever nossa própria função de alta ordem:
public static List<T> Filter<T>(List<T> list, Func<T, bool> predicate){ var result = new List<T>(); foreach (var item in list) if (predicate(item)) result.Add(item); return result;}4. LINQ: Poderosa Ferramenta de Estilo Funcional
LINQ (Language Integrated Query) é, talvez, o exemplo mais marcante de programação funcional em C#. Ele permite processar coleções de forma declarativa, como em SQL.
Exemplo: obter nomes de usuários maiores de 18 anos, ordenados por idade:
var users = new List<User>{ new("Anna", 22), new("Boris", 17), new("Victor", 30)};
var result = users .Where(u => u.Age >= 18) .OrderBy(u => u.Age) .Select(u => u.Name) .ToList();LINQ suporta execução tardia (lazy evaluation), economizando recursos.
5. Mônadas e Tipo Maybe em C#
Mônadas são uma abstração para encadeamento de computações com contexto (por exemplo, tratamento de null ou assincronia). Em C#, não há uma mônada Maybe embutida, mas é fácil implementá-la:
public class Maybe<T>{ private readonly T _value; public bool HasValue { get; } p