Programação Funcional em C++: Nível Avançado
\n\nProgramação Funcional (PF) é um paradigma que considera a computação como a avaliação de funções matemáticas e evita estado mutável e efeitos colaterais. Embora C++ seja tradicionalmente associado à programação orientada a objetos e procedural, os padrões modernos (C++11 e posteriores) introduziram ferramentas poderosas para escrever código funcional. Neste artigo, iremos além das lambdas básicas e exploraremos técnicas avançadas: desde composição de funções até a emulação de mônadas.
\n\n1. Composição de Funções e Pipelines
\n\nComposição de funções é o processo de combinar duas ou mais funções para criar uma nova função. Em C++, é conveniente usar expressões lambda e templates para isso.
\n\n#include <iostream>\n#include <functional>\n#include <vector>\n#include <algorithm>\n\n// Template para composição de duas funções\ntemplate<typename F, typename G>\nauto compose(F f, G g) {\n return [f, g](auto x) {\n return f(g(x));\n };\n}\n\n// Exemplo de uso\nint main() {\n auto addTwo = [](int x) { return x + 2; };\n auto multiplyByThree = [](int x) { return x * 3; };\n\n // Composição: primeiro multiplyByThree, depois addTwo\n auto composed = compose(addTwo, multiplyByThree);\n\n std::cout << composed(5) << std::endl; // (5*3) + 2 = 17\n\n // Processamento em pipeline com std::ranges (C++20)\n std::vector<int> numbers = {1, 2, 3, 4, 5};\n auto result = numbers\n | std::views::filter([](int n) { return n % 2 == 0; })\n | std::views::transform([](int n) { return n * n; });\n\n for (int n : result) {\n std::cout << n << " "; // 4 16\n }\n return 0;\n}\n\n\nPipelines com std::views (C++20) permitem construir cadeias de transformações sem criar contêineres intermediários, o que é uma abordagem funcional clássica.
2. Currying e Aplicação Parcial
\n\nCurrying é a transformação de uma função que recebe vários argumentos em uma sequência de funções, cada uma recebendo um único argumento. A aplicação parcial fixa parte dos argumentos.
\n\n#include <iostream>\n#include <functional>\n\n// Função de adição com currying\nauto curriedAdd = [](int a) {\n return [a](int b) {\n return [a, b](int c) {\n return a + b + c;\n };\n };\n};\n\n// Aplicação parcial com std::bind\nint multiply(int a, int b, int c) {\n return a * b * c;\n}\n\nint main() {\n // Usando currying\n auto add5 = curriedAdd(5);\n auto add5And3 = add5(3);\n std::cout << add5And3(2) << std::endl; // 10\n\n // Aplicação parcial via std::bind\n using namespace std::placeholders;\n auto multiplyBy2 = std::bind(multiply, _1, _2, 2);\n std::cout << multiplyBy2(3, 4) << std::endl; // 3*4*2 = 24\n\n // Aplicação parcial via lambda (mais idiomático em C++14+)\n auto multiplyBy3 = [](int a, int b) { return multiply(a, b, 3); };\n std::cout << multiplyBy3(5, 6) << std::endl; // 5*6*3 = 90\n\n return 0;\n}\n\n\nCurrying é especialmente útil ao trabalhar com funções de ordem superior, como std::transform ou std::accumulate, permitindo criar versões especializadas de funções "em tempo real".
3. Funtores, Aplicativos e Emulação de Mônadas
\n\nEm Haskell, mônadas são um padrão poderoso para lidar com efeitos colaterais. Em C++, podemos emulá-las usando templates de classes. Vamos considerar uma mônada Maybe simples (análoga a std::optional), mas com operações monádicas.
#include <iostream>\n#include <functional>\n#include <optional>\n\n// Mônada Maybe (wrapper sobre std::optional)\ntemplate<typename T>\nclass Maybe {\n s