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
Was this helpful?