Conceitos de linguagens funcionais: o que é currying?

O paradigma funcional e suas vertentes vêm ganhando cada vez mais destaque no mercado de maneira geral. Os benefícios das linguagens funcionais que vimos neste artigo justificam essa adoção mais intensa de soluções e linguagens que suportem este paradigma por parte do mercado.
Porém, junto com esta popularização, começam a surgir uma variedade de termos que antes eram incomuns e soam como algo extremamente complexo, como currying, pattern matching e monads, por exemplo. E, por muitas vezes, estes termos acabam por assustar quem está tendo os primeiros contatos com o mundo do paradigma funcional.
Nesta série de artigos, vamos começar a ilustrar alguns destes termos esquisitos. Iremos iniciar falando um pouco sobre currying.

JavaScript Básico
Curso de JavaScript Básico
CONHEÇA O CURSO

Antes de tudo: quem foi Haskell Curry?

Toda essa história começa com um matemático norte-americano: Haskell Curry. Haskell inicialmente iniciou seus estudos superiores em medicina na Universidade de Harvard em 1916, mas decidiu estudar matemática no meio do caminho. Ainda trabalhou no campo de engenharia elétrica no MIT e logo após retornou à Harvard para obter uma pós-graduação em Física. Logo após, ainda obteve o título de Ph.D. na área de lógica combinatória, também pela Universidade de Harvard. Curry ainda trabalhou no projeto ENIAC (o primeiro computador digital do mundo) após a Segunda Guerra Mundial.

Haskell Curry (Gleb.svechnikov [CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0)])

A foto acima pertence a Gleb.svechnikov (CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0))

Curry fez um trabalho fantástico na área de lógica combinatória, área esta que era sua grande paixão. Seu trabalho matemático envolvendo lógica e análise combinatória inspirou e norteou uma série de princípios na área da computação, principalmente no que diz respeito justamente ao paradigma funcional e no cálculo-lambda (uma das bases do paradigma funcional). Além disso, Curry fundamentou um conceito importantíssimo para o paradigma funcional, conceito este que, em sua homenagem, recebeu seu sobrenome: currying. Ainda temos uma outra homenagem a Curry: a famosa linguagem funcional Haskell, que recebeu esse nome em homenagem ao matemático precursor dos conceitos de programação funcional.

O que é o currying em linguagens funcionais?

A definição clássica de currying diz o seguinte:

Dada uma função f do tipo f: (X x Y) → Z, a técnica de currying pode transformar a expressão em curry(f): → (Y → Z)

Calma! Não se desespere com a notação matemática. Ela é mais simples do que parece. 🙂
Vamos relembrar um conceito importante sobre funções matemáticas: elas não podem receber mais do que um parâmetro. Por definição, funções matemáticas só recebem um único parâmetro por vez.
Se levarmos esta situação para o ambiente de programação, criar métodos que recebem um único parâmetro por vez pode ser um complicador. Mas, se quisermos seguir o paradigma funcional, precisamos tentar seguir estas regras… Como podemos criar funções que recebam um único parâmetro por vez? Como poderíamos, por exemplo, criar uma função para multiplicar dois números recebendo um único parâmetro?
Vamos adotar o JavaScript para ilustrar esta situação. Se fôssemos escrever essa função de maneira “normal”, poderíamos ter o seguinte código:

function multiplicar(n1, n2) {
    return n1 * n2;
}
// ...
console.log(multiplicar(2, 2)); // A saída deverá ser "4"

Nossa função está recebendo dois parâmetros, e precisamos que ela receba um único parâmetro. Nós podemos obter esse resultado se montarmos uma composição de funções: podemos ter a primeira função recebendo n1, sendo que esta função retorna uma outra função que recebe n2! Nosso código poderia ficar da seguinte maneira:

function multiplicar(n1) {
    return function(n2) {
        return n1 * n2;
    }
}
// ...
const funcaoCurrying = multiplicar(2);
console.log(funcaoCurrying(2)); // A saída deverá ser "4"
// OU...
console.log(multiplicar(2)(2)); // A saída deverá ser "4"

Veja que temos funções que recebem um único parâmetro por vez, e estas funções podem retornar outras funções, formando uma cadeia de composição de funções. É exatamente o que a notação curry(f): → (Y → Z) na notação matemática que vimos no começo deste bloco diz: nós podemos ter uma função chamada curry gerada pelo encadeamento das funções Y e Z. No final, todas essas funções são funções que chamamos de “unárias”, pois elas lidam somente com um único parâmetro por vez.
O código acima ficaria muito mais elegante se utilizássemos arrow functions (ou funções-lambda):

const multiplicar = (n1) => (n2) => n1 * n2;
// ...
console.log(multiplicar(2)(2)); // A saída deverá ser "4"

Com o trecho acima, temos um código simples, elegante, funcional e utilizando a técnica de currying! 😉

Python - Fundamentos
Curso de Python - Fundamentos
CONHEÇA O CURSO

Quais as vantagens obtidas através da utilização do currying?

Para mim, particularmente, nós temos uma série de vantagens interessantes com o currying:

  • Reaproveitamento de código. Utilizando currying, podemos criar uma série de funções genéricas que, quando combinadas, podem chegar à funcionalidade que desejamos, aplicando-se o conceito clássico de composição com maestria;
  • Criação de métodos que podem receber múltiplos parâmetros de maneira “emulada” em linguagens puramente funcionais: algumas linguagens funcionais deixam o conceito de currying implícito: basta você criar uma função que receba mais de um parâmetro que, internamente, ela será decomposta para múltiplas funções aplicando-se a técnica de currying. Porém, outras linguagens, como o Haskell, não aceitam essa abordagem de nenhuma maneira. Estas linguagens são conhecidas como linguagens puramente funcionais ou linguagens funcionais puras. Nestes casos, se quisermos criar métodos que recebam mais do que um parâmetro (o que é bem comum), nós precisaremos recorrer ao currying inevitavelmente;
  • A utilização do currying produz código elegante e conciso, mesmo em linguagens não funcionais. Currying não é um conceito que é aplicável somente em linguagens funcionais. Você pode aplicar a técnica de currying em linguagens que não são majoritariamente funcionais, como Java e C#, produzindo código legível e de fácil manutenção em praticamente qualquer linguagem;
  • No final, temos funções unárias. Funções unárias – as funções que recebem um único parâmetro – são mais legíveis e mais fáceis de serem manuteídas, pois geralmente são menores e possuem menos pontos de entrada (já que possuem um único parâmetro), o que reduz os possíveis pontos de efeito colateral durante uma alteração no código;
  • Você provavelmente já utiliza a técnica de currying. Se você analisar as APIs de map/reduce/filter do JavaScript, o LINQ do C# ou a stream API do Java, você verá que as APIs já trabalham com o conceito de funções que recebem um único parâmetro, ou mesmo funções que recebem funções como argumentos. Boa parte dos métodos destas APIs já utiliza conceitos de linguagens funcionais ,como o currying. Estas APIs só conseguem ser tão genéricas justamente por causa desse conceito de composição de funções que é natural ao conceito de currying.

Ainda temos muitos outros termos para vermos!

A programação funcional é de fato um mundo cheio de termos e jargões que podem soar esquisitos no início, mas que geralmente são mais simples do que parecem (como o conceito de currying). Ainda temos vários outros termos para serem explorados, como monads e partial function applications. Nos próximos posts, iremos continuar a desbravar estes termos esquisitos e que colocam medo por várias vezes em nós; mas que, no final, são mais simples do que parecem.
Até o próximo post! 😉

Deixe seu comentário

Líder de Conteúdo/Inovação. Pós-graduado em Projeto e Desenvolvimento de Aplicações Web. Certificado Microsoft MCSD (Web).