Currying e Aplicação Parcial

Currying e aplicação parcial são dois conceitos bastante presentes na programação funcional. Os dois conceitos operam diretamente em funções com o objetivo de alterar seu tipo.

Primeiro vamos entender como exemplificar o tipo de uma função, para isso, vamos utilizar uma sintaxe parecida com a sintaxe utilizada na linguagem F#, também da plataforma .NET.

Em F# o tipo da função é definido por: parâmetro -> retorno

Logo, uma função com um parâmetro do tipo int e um retorno do tipo bool pode ser descrita como: int -> bool

Quando houverem múltiplos parâmetros iremos representar aqui (diferente do F#) como: int, int, int -> bool

Com isso, estabelecemos uma sintaxe para definir o tipo de uma função.

Agora vamos entender o problema que estas duas técnicas auxiliam na resolução.

A programação funcional é baseada principalmente no conceito de funções. Por definição, uma função matemática deve possuir apenas um parâmetro (domínio) e um retorno (alcance).

Logo, isso implicaria que, todas as funções escritas em um código funcional devem conter apenas um parâmetro, como fazer isso? - Currying

Entendendo Currying

O processo de currying consiste em quebrar funções de N parâmetros em N funções, onde cada função irá receber apenas um parâmetro e retornar uma nova função que espera os parâmetros restantes.

Logo, uma função de soma, que seria representada por: int, int -> int

Ao passar pelo processo de currying pode ser representada por: int -> int -> int

Vamos realizar um passo-a-passo do processo de currying em uma função simples, que realiza a soma de 2 valores.

Primeiro vamos definir a função como:

Func<int, int, int> add = 
    (value, value2) => value + value2;

Podemos utilizar esta função normalmente, conforme código:

int result
Func<int, int, int> add = (value, value2) => value + value2;
result = add(2, 3);
//result = 5

Ao realizar o processo de currying na função add o retorno será uma função do tipo Func<int, Func<int,int>>, ou seja, será uma função que receberá um valor inteiro e retornará um nova função esperando o segundo valor inteiro.

Quando esta segunda função receber o último parâmetro ela irá realizar o processamento proposto por add.

Veja como podemos realizar o currying com a Tango.

Func<int, int, int> add = 
    (value, value2) => value + value2;

Func<int, Func<int, int>> addCurried = Currying.Curry(add);
curriedResult = addCurried(2)(3);
//curriedResult = 5

Veja que a forma de informar os parâmetros na função addCurried é um pouco diferente, isso porque ele gera uma série de funções, onde cada uma espera apenas um parâmetro.

A Tango também fornece o Curry através de métodos de extensão para os delegates Func, portanto ao você também poderá realizar a operação descrita anteriormente da seguinte forma:

Func<int, int, int> add =
(value, value2) => value + value2;

//Func<int, Func<int, int>> addCurried = Currying.Curry(add);
Func<int, Func<int, int>> addCurried = add.Curry();
curriedResult = addCurried(2)(3);
//curriedResult = 5

Entendendo a Aplicação Parcial

A aplicação parcial é um pouco diferente do processo de Currying, mas também envolve a questão dos tipos de uma função.

Através da aplicação parcial é possível realizar a chamada de um método sem informarmos todos os parâmetros. Como resultado desta operação, será retornada uma nova função que espera todos os parâmetros restantes.

Veja as diferenças entre Currying e aplicação parcial em uma função que soma três números inteiros.

Esta seria o tipo da função de soma: int, int, int -> int

Ao realizar o Currying nesta função o resultado obtido seria: int -> int -> int -> int

Neste ponto a aplicação parcial funciona completamente diferente do processo de Currying, poderíamos inclusive, realizar diferentes aplicações parciais nesta função.

Ao informar apenas um parâmetro o resultado obtido seria: int, int -> int

Informando dois parâmetros o resultado obtido seria: int -> int

Similar ao processo de Currying, a operação fundamental descrita pela função só é executada quando todos os parâmetros forem informados, independente da quantidade de funções intermediárias geradas.

Veja a implementação descrita, primeiro através de currying:

Func<int, int, int, int> add = 
    (value, value2) => value + value2;

Func<int, Func<int, Func<int, int>>> addCurried = add.Curry();
curriedResult = addCurried(2)(3)(5);
//curriedResult = 10

Utilizando aplicação parcial com apenas um parâmetro:

Func<int, int, int, int> add = 
    (value, value2) => value + value2;

Func<int, int, int> addPartialApplied = add.PartialApply(2);
partialAppliedResult = addPartialApplied (3,5);
//partialAppliedResult = 10

Utilizando aplicação parcial com dois parâmetros:

Func<int, int, int, int> add = 
    (value, value2) => value + value2;

Func<int, int> addPartialApplied = add.PartialApply(2,3);
partialAppliedResult = addPartialApplied(5);
//partialAppliedResult = 10

Todos os métodos disponíveis para aplicação parcial e Currying operam em métodos de até 4 parâmetros, podendo retornar qualquer tipo, inclusive void.

Além disso, eles podem ser utilizados como métodos estáticos das classes Currying e PartialApplication ou como extensões para Func e Action.

Last updated