Currying é o processo de transformar uma função que espera vários argumentos em uma função que espera um único argumento e retorna outra função curried. Por exemplo, uma função que recebe três argumentos, ao sofrer currying, resulta em uma função que recebe um argumento e retorna uma função que recebe um argumento, que por sua vez retorna uma função que recebe um argumento e retorna o resultado da função original.

Aplicação parcial de uma função corresponde a chamar a função passando menos argumentos do que a função recebe.

Considere a função produto:

function produto(a, b) {
    return a * b;
}

Do jeito que a função está definida, não faz sentido realizar uma aplicação parcial:

A chamada produto(2) equivale à chamada produto(2, undefined), resultado na multiplicação 2 * undefined, cujo resultado é NaN (not a number).

Usando closures para implementar funções curried

Uma versão curried da função (i.e., função resultante do currying) pode ser escrita usando-se closures:

Outra forma de escrever:

Exercício básico de currying

Vamos praticar? Considere a seguinte função de 3 argumentos, que calcula o valor de uma função de primeiro grau em determinado ponto:

Vamos escrever uma versão curried dessa função? Essa versão deve receber a e retornar uma função que recebe b, que retorna uma função que recebe x e retorna a resposta.

Aplicação prática de currying: conversor de temperaturas

Dá pra ver que, na versao curried, a maneira de chamar a função fica diferente: usamos (3)(2)(42) em vez de (3, 2, 42). Isso porque cada chamada, exceto a última, retorna uma função, e por isso precisamos fazer 3 chamadas em vez de uma.

A vantagem de escrever uma função curried é que fica mais fácil construir funções com base nas funções curried fixando alguns argumentos. Por exemplo, a função celsiusParaFahrenheit pode ser escrita assim:

Até agora nós realizamos o currying das funções de forma manual, reescrevendo o código das funções. Em algumas linguagens, como Haskell, todas as funções são curried (e, portanto, admitem aplicação parcial). Isso não acontece com Javascript, mas há bibliotecas capazes de realizar o currying de qualquer função.

A biblioteca Ramda

A biblioteca Ramda possui uma função, R.curry, que recebe uma função e retorna uma versão curried dessa função.

A biblioteca possui outras funções, como R.map, R.filter, R.reduce e R.length. Todas as funções da biblioteca Ramda já são curried e, portanto, admitem aplicação parcial.

O currying feito pela biblioteca Ramda é mais sofisticado, pois permite uma aplicação parcial com um, dois ou mais argumentos, retornando uma função que recebe os argumentos restantes:

Aplicação prática: uso com map

Currying é útil em conjunção de funções de alta ordem. Considere a função arr.map(f), que retorna um array resultante da aplicação da função f a cada elemento de arr. A função map chama f passando um único argumento; como podemos então usar nossa função primeiroGrau para mapear elementos de um array? Usando currying:

Ordem dos parâmetros

Se você quer criar funções que podem ser curried, vale a pena pensar na ordem dos seus argumentos. O ideal é deixar concentrar os argumentos que tendem a ser fixados no início, e deixar os argumentos que variam mais no final da lista de argumentos. Note o exemplo da função primeiroGrau(a, b, x): normalmente, quando chamamos a função várias vezes, fixamos a e b e variamos x; por isso x foi colocado como último argumento.

Composição de funções

(Baseado em https://medium.com/@collardeau/intro-to-functional-programming-concepts-in-javascript-b0650773139c e https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch5.html)

Considere o problema de obter a última inicial do nome de uma pessoa. Podemos decompor esse problema em dois: obter o último nome de uma pessoa e obter a primeira letra de um nome:

function getUltimoNome(nomeCompleto) { return nomeCompleto.split(" ").splice(-1)[0]; }
function getPrimeiraLetra(string) { return string[0]; }

A função que retorna a última inicial de um nome pode então ser escrita como uma composição das funções getUltimoNome e getPrimeiraLetra:

Lembre-se do conceito de composição de funções da matemática: se eu tenho as funções f(x), g(x) e h(x), a composição dessas funções, na ordem apresentada, é uma nova função, c(x) = h(g(f(x))).

A biblioteca Ramda fornece uma função de alta ordem para compor duas ou mais funções quaisquer: R.pipe. No exemplo da matemática, c(x) = R.pipe(f, g, h). O exemplo da última inicial de um nome fica assim:

(shell scripting também usa o conceito de pipe)

Esse estilo de programar é chamado de pointfree, pois a função getUltimaInicial é definida sem declarar quais são os seus parâmetros.

Exercício de composição de funções

Agora é com você. Crie uma função para retornar a quantidade de elementos de um array cujo quadrado é ímpar.

Você pode ignorar o fato de que o quadrado de um número é ímpar se, e somente se o número é impar.

Composição de funções e número de parâmetros

Na composição de funções, o retorno da primeira função é passado como parâmetro na chamada da segunda função, o retorno da segunda função é passado como parâmetro na chamada da terceira função, e assim por diante.

Na composição de funções, todas as funções a partir da segunda devem ser funções de um único parâmetro. Você sabe explicar por quê?

Vamos ver um exemplo agora em que a primeira função da composição recebe mais de um parâmetro.

Por que a primeira função de uma composição pode ser uma função de dois ou mais parâmetros?

Vamos relembrar o exemplo anterior:

A função filtraImpar é uma função de um parâmetro; por isso ela pode ser usada como segunda função de uma composição. Essa função pode ser escrita de forma mais compacta usando aplicação parcial: filtraImpar = R.filter(x => x % 2 == 1) (note que R.filter é uma função curried, assim como todas as funções da biblioteca Ramda). Com essa observação, modifique o código abaixo, substituindo /*...*/ por aplicações parciais de R.map, R.filter e R.length, de forma a obter o mesmo comportamento do código acima.

A biblioteca Ramda também possui a função compose, que faz a mesma coisa que a função pipe, porém recebendo os argumentos na ordem inversa.

Exercícios: https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch5.html

Curiosidade

Pipes são extremamente úteis para escrever transformações de dados. Na linguagem R, uma biblioteca introduziu o operador de pipe, %>%, que é comumente usado para aplicar sequências de transformações sobre uma tabela. Para saber mais, leia Simpler R coding with pipes > the present and future of the magrittr package.

Exercício 1

Refatore o código para remover todos os argumentos através da aplicação parcial da função. Considere a função wordsOriginal, abaixo, e escreva a função equivalente words, através da aplicação parcial de R.split:

Exercício 1a

Use map para criar uma nova função, wordsArray, que aplica words a cada elemento da array de entrada.

Exercício 2

A função filterQs recebe um array de palavras e retorna apenas as palavras que possuem a letra q. Crie uma versão pointfree dessa função, usando composição de funções parcialmente aplicadas:

Exercício 3

Use a função auxiliar _mantemMaior para criar uma versão pointfree de max:

Baseado em https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch4.html