Introdução à programação funcional em nível avançado
\n\nProgramação funcional (PF) é um paradigma que trata a computação como a execução de funções matemáticas e evita a mudança de estado e dados mutáveis. Em JavaScript, especialmente no ambiente Node.js, a PF está se tornando cada vez mais popular devido à sua previsibilidade, testabilidade e paralelismo.
\n\nSe você já domina os fundamentos da PF (funções puras, funções de ordem superior, map, filter, reduce), é hora de passar para o próximo nível. Neste artigo, abordaremos conceitos avançados: currying, composição, mônadas e trabalho com dados imutáveis.
1. Currying e aplicação parcial
\n\nO que é currying?
\nCurrying é o processo de transformar uma função que recebe vários argumentos em uma sequência de funções, cada uma recebendo um único argumento. Isso permite criar funções especializadas com base em funções gerais.
\n\n// Função normal\nconst add = (a, b, c) => a + b + c;\nconsole.log(add(1, 2, 3)); // 6\n\n// Versão curried\nconst curriedAdd = a => b => c => a + b + c;\nconsole.log(curriedAdd(1)(2)(3)); // 6\n\n// Função universal de currying\nconst curry = (fn) => {\n const arity = fn.length;\n const resolver = (...args) => {\n if (args.length < arity) {\n return (...nextArgs) => resolver(...args, ...nextArgs);\n }\n return fn(...args);\n };\n return resolver;\n};\n\nconst multiply = (a, b, c) => a * b * c;\nconst curriedMultiply = curry(multiply);\nconsole.log(curriedMultiply(2)(3)(4)); // 24\nconsole.log(curriedMultiply(2, 3)(4)); // 24\n\nAplicação parcial
\nAplicação parcial é a fixação de parte dos argumentos de uma função, criando uma nova função com menos argumentos. Ao contrário do currying, a aplicação parcial não exige uma sequência estrita de chamadas.
\n\nconst partial = (fn, ...presetArgs) => (...laterArgs) => fn(...presetArgs, ...laterArgs);\n\nconst greet = (greeting, name) => `${greeting}, ${name}!`;\nconst sayHello = partial(greet, 'Hello');\nconst sayHi = partial(greet, 'Hi');\n\nconsole.log(sayHello('Alice')); // Hello, Alice!\nconsole.log(sayHi('Bob')); // Hi, Bob!\n\n2. Composição de funções
\n\nComposição avançada
\nComposição é o processo de combinar várias funções para criar uma nova função. Na PF avançada, usamos composição da direita para a esquerda (compose) e da esquerda para a direita (pipe).
// Composição simples (da direita para a esquerda)\nconst compose = (...fns) => (value) => fns.reduceRight((acc, fn) => fn(acc), value);\n\n// Composição da esquerda para a direita (pipe)\nconst pipe = (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value);\n\n// Exemplo: processamento de string\nconst trim = (str) => str.trim();\nconst toLowerCase = (str) => str.toLowerCase();\nconst capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);\nconst removeSpaces = (str) => str.replace(/\\s+/g, '-');\n\nconst slugify = compose(capitalize, toLowerCase, trim, removeSpaces);\n// Equivalente: removeSpaces(trim(toLowerCase(capitalize(" Hello World "))))\n\nconsole.log(slugify(' Hello World ')); // Hello-world\n\nTransdutores (Transducers)
\nTransdutores são a composição de funções de transformação que operam em qualquer estrutura de dados iterável. Eles permitem combinar várias operações (por exemplo, map e filter) em uma única passagem.
const transducer = (mapper, predicate) => (reducer) => (acc, value) => {\n const mapped = mapper(value);\n return predicate(mapped) ? reducer(acc, mapped) : acc;\n};\n\nconst double = (x) => x * 2;\nconst isEven = (x) => x % 2 === 0;\n\nconst processNumbers = transducer(double, isEven);\nconst sumReduc