Eu tenho lido artigos sobre programação funcional todos os dias e tentado aplicar algumas práticas o máximo possível. Mas não entendo o que é único no curry ou na aplicação parcial.
Tome este código Groovy como um exemplo:
def mul = { a, b -> a * b }
def tripler1 = mul.curry(3)
def tripler2 = { mul(3, it) }
Eu não entendo qual é a diferença entre tripler1
e tripler2
. Eles não são os mesmos? O 'currying' é suportado em linguagens funcionais puras ou parciais, como Groovy, Scala, Haskell, etc. função ou fechamento que encaminhará os parâmetros para a função original (como tripler2
) na maioria dos idiomas (até C.)
Estou faltando alguma coisa aqui? Há lugares em que posso usar curry e aplicação parcial no meu aplicativo Grails, mas hesito em fazê-lo porque estou me perguntando "Qual é a diferença?"
Por favor me esclareça.
EDIT: Vocês estão dizendo que aplicação parcial / currying é simplesmente mais eficiente do que criar / chamar outra função que encaminha parâmetros padrão para a função original?
fonte
f x y = x + y
significa quef
é uma função que aceita um parâmetro int. O resultado def x
(f
aplicado ax
) é uma função que aceita um parâmetro int. O resultadof x y
(ou(f x) y
seja,f x
aplicado ay
) é uma expressão que não aceita parâmetros de entrada e é avaliada pela reduçãox + y
.Respostas:
Currying consiste em transformar / representar uma função que recebe n entradas em n funções em que cada uma recebe 1 entrada. Aplicação parcial é sobre como fixar algumas das entradas em uma função.
A motivação para a aplicação parcial é principalmente o fato de facilitar a gravação de bibliotecas de funções de ordem superior. Por exemplo, todos os algoritmos do C ++ STL usam predicados ou funções unárias, o bind1st permite que o usuário da biblioteca conecte funções não unárias com um valor vinculado. O escritor da biblioteca, portanto, não precisa fornecer funções sobrecarregadas para todos os algoritmos que executam funções unárias para fornecer versões binárias
O curry em si é útil porque oferece aplicação parcial em qualquer lugar que você desejar de graça, ou seja, você não precisa mais de uma função
bind1st
para aplicar parcialmente.fonte
currying
algo específico para o groovy ou aplicável em vários idiomas?E o otimizador analisará isso e seguirá rapidamente para algo que possa entender. O curry é um pequeno truque para o usuário final, mas possui benefícios muito melhores do ponto de vista do design de linguagem. É muito bom lidar com todos os métodos como unários,
A -> B
ondeB
pode haver outro método.Ele simplifica os métodos que você precisa escrever para lidar com funções de ordem superior. Sua análise estática e otimização no idioma têm apenas um caminho para trabalhar com esse comportamento de maneira conhecida. A ligação de parâmetros simplesmente cai fora do design, em vez de exigir a utilização de argolas para executar esse comportamento comum.
fonte
Como @jk. Como mencionado, o currying pode ajudar a tornar o código mais geral.
Por exemplo, suponha que você tenha essas três funções (em Haskell):
A função
f
aqui assume duas funções como argumentos, passa1
para a primeira função e passa o resultado da primeira chamada para a segunda função.Se chamássemos
f
usingq
er
como argumentos, estaria efetivamente:onde
q
seria aplicado1
e retornaria outra função (comoq
é exibida); essa função retornada seria então passada parar
como argumento a ser dado um argumento de3
. O resultado disso seria um valor de9
.Agora, digamos que tivemos duas outras funções:
também podemos passar esses valores
f
e obter um valor de7
or15
, dependendo de nossos argumentos serems t
ort s
. Como essas funções retornam um valor em vez de uma função, nenhuma aplicação parcial ocorreria emf s t
ouf t s
.Se tivéssemos escrito
f
comq
er
em mente, poderíamos ter usado um lambda (função anônima) em vez de aplicação parcial, por exemplo:mas isso teria restringido a generalidade de
f'
.f
pode ser chamado com argumentosq
er
ous
et
, masf'
só pode ser chamado comq
er
-f' s t
ef' t s
ambos resultam em um erro.MAIS
Se
f'
fosse chamado com um parq'
/r'
emq'
que o argumento tivesse mais de dois argumentos,q'
ele ainda acabaria sendo parcialmente aplicadof'
.Como alternativa, você pode quebrar
q
fora ouf
não dentro, mas isso deixaria uma lambda aninhada desagradável:que é essencialmente o que o caril
q
estava em primeiro lugar!fonte
def f = { a, b -> b a.curry(1) }
para fazerf q, r
para trabalhar edef f = { a, b -> b a(1) }
oudef f = { a, b -> b a.curry(1)() }
paraf s, t
trabalhar. Você precisa passar todos os parâmetros ou dizer explicitamente que está currying. :(f x y
, o que muitos idiomas escreveriamf(x)(y)
, nãof(x, y)
. Talvez seu código funcione no Groovy se você escreverq
para que ele seja chamado comoq(1)(2)
?(partial f a b ...)
- já que estou acostumado a Haskell, sinto muita falta de curry adequado ao programar em outras linguagens (embora eu esteja trabalhando recentemente no F #, que felizmente o suporta).Existem dois pontos principais sobre a aplicação parcial. A primeira é sintática / conveniente - algumas definições se tornam mais fáceis e curtas de ler e escrever, como o @jk mencionou. (Confira a programação do Pointfree para saber mais sobre como isso é incrível!)
O segundo, como @telastyn mencionou, é sobre um modelo de funções e não é apenas conveniente. Na versão Haskell, da qual vou obter meus exemplos porque não estou familiarizado com outras linguagens com aplicação parcial, todas as funções usam um único argumento. Sim, até funções como:
pegue um único argumento; devido à associatividade do construtor do tipo de função
->
, o acima é equivalente a:que é uma função que recebe
a
e retorna uma função[a] -> [a]
.Isso nos permite escrever funções como:
que pode aplicar qualquer função a um argumento do tipo apropriado. Mesmo os loucos, como:
Ok, então esse foi um exemplo artificial. Mas uma mais útil envolve a classe do tipo Aplicative , que inclui este método:
Como você pode ver, o tipo é idêntico
$
se você remover oApplicative f
bit e, de fato, essa classe descreve a aplicação de funções em um contexto. Então, em vez da aplicação da função normal:Podemos aplicar funções em um contexto Aplicativo; por exemplo, no contexto Talvez, em que algo pode estar presente ou ausente:
Agora, a parte mais interessante é que a classe do tipo Applicative não menciona nada sobre funções de mais de um argumento - no entanto, pode lidar com elas, inclusive funções de 6 argumentos como
f
:Até onde eu sei, a classe do tipo Aplicativo em sua forma geral não seria possível sem alguma concepção de aplicação parcial. (Para qualquer especialista em programação existente - corrija-me se eu estiver errado!) É claro que, se sua linguagem não possui aplicação parcial, você pode construí-la de alguma forma, mas ... não é a mesma coisa, é? ? :)
fonte
Applicative
sem currying ou aplicação parcial usariafzip :: (f a, f b) -> f (a, b)
. Em um idioma com funções de ordem superior, isso permite elevar a aplicação de currying e parcial no contexto do functor e é equivalente a(<*>)
. Sem funções de ordem superior, você não terá,fmap
então a coisa toda seria inútil.f <$> x <*> y
estilo idiomático encantador funciona facilmente porque o curry e a aplicação parcial funcionam facilmente. Em outras palavras, o que é agradável é mais importante do que é possível aqui.