He estado leyendo artículos sobre programación funcional todos los días y he tratado de aplicar algunas prácticas tanto como sea posible. Pero no entiendo qué es único en el curry o la aplicación parcial.
Tome este código Groovy como ejemplo:
def mul = { a, b -> a * b }
def tripler1 = mul.curry(3)
def tripler2 = { mul(3, it) }
No entiendo cuál es la diferencia entre tripler1
y tripler2
. ¿No son los dos iguales? El 'curry' es compatible con lenguajes funcionales puros o parciales como Groovy, Scala, Haskell, etc. Pero puedo hacer lo mismo (left-curry, right-curry, n-curry o aplicación parcial) simplemente creando otro nombre o anónimo función o cierre que reenviará los parámetros a la función original (como tripler2
) en la mayoría de los idiomas (incluso C.)
¿Me estoy perdiendo de algo? Hay lugares donde puedo usar el currículum y la aplicación parcial en mi solicitud de Grails, pero dudo en hacerlo porque me pregunto "¿Cómo es eso diferente?"
Por favor iluminame.
EDITAR: ¿Están diciendo que la aplicación / currículum parcial es simplemente más eficiente que crear / llamar a otra función que reenvía los parámetros predeterminados a la función original?
fuente
f x y = x + y
significa quef
es una función que toma un parámetro int. El resultado def x
(f
aplicado ax
) es una función que toma un parámetro int. El resultadof x y
(o(f x) y
, es decir,f x
aplicado ay
) es una expresión que no toma parámetros de entrada y se evalúa reduciendox + y
.Respuestas:
El curry se trata de convertir / representar una función que toma n entradas en n funciones que cada una toma 1 entrada. La aplicación parcial se trata de arreglar algunas de las entradas a una función.
La motivación para la aplicación parcial es principalmente que facilita la escritura de bibliotecas de funciones de orden superior. Por ejemplo, los algoritmos en C ++ STL toman en gran medida predicados o funciones unarias, bind1st permite al usuario de la biblioteca enganchar funciones no unarias con un valor enlazado. Por lo tanto, el escritor de la biblioteca no necesita proporcionar funciones sobrecargadas para todos los algoritmos que toman funciones unarias para proporcionar versiones binarias
El curring en sí mismo es útil porque le brinda una aplicación parcial en cualquier lugar que desee de forma gratuita, es decir, ya no necesita una función como
bind1st
aplicar parcialmente.fuente
currying
algo específico para groovy o aplicable en todos los idiomas?Y el optimizador lo verá y pasará rápidamente a algo que pueda entender. El curry es un pequeño y agradable truco para el usuario final, pero tiene beneficios mucho mejores desde el punto de vista del diseño del lenguaje. Es realmente agradable manejar todos los métodos como unarios,
A -> B
dondeB
puede haber otro método.Simplifica qué métodos tiene que escribir para manejar funciones de orden superior. Su análisis estático y optimización en el idioma solo tiene una ruta para trabajar que se comporta de manera conocida. La unión de parámetros simplemente se cae del diseño en lugar de requerir que los aros hagan este comportamiento común.
fuente
Como @jk. Aludido, curry puede ayudar a hacer el código más general.
Por ejemplo, suponga que tiene estas tres funciones (en Haskell):
La función
f
aquí toma dos funciones como argumentos, pasa1
a la primera función y pasa el resultado de la primera llamada a la segunda función.Si tuviéramos que llamar
f
usandoq
yr
como argumentos, efectivamente estaría haciendo:donde
q
se aplicaría1
y devolvería otra función (comoq
se curry); esta función devuelta se pasaría ar
su argumento para que se le dé un argumento de3
. El resultado de esto sería un valor de9
.Ahora, digamos que teníamos otras dos funciones:
podríamos pasar esto
f
también y obtener un valor de7
o15
, dependiendo de si nuestros argumentos erans t
ot s
. Dado que estas funciones devuelven un valor en lugar de una función, no se llevaría a cabo una aplicación parcial enf s t
of t s
.Si hubiéramos escrito
f
conq
yr
en mente, podríamos haber usado una lambda (función anónima) en lugar de una aplicación parcial, por ejemplo:pero esto habría restringido la generalidad de
f'
.f
se puede llamar con argumentosq
yr
os
yt
, perof'
solo se puede llamar conq
yr
,f' s t
yf' t s
ambos producen un error.MÁS
Si
f'
se llamara con unq'
/r'
par dondeq'
tomaron más de dos argumentos, elq'
todavía terminaría siendo aplicado parcialmentef'
.Alternativamente, podría envolver
q
afuera enf
lugar de adentro, pero eso lo dejaría con una desagradable lambda anidada:que es esencialmente lo que el curry
q
era en primer lugar!fuente
def f = { a, b -> b a.curry(1) }
para hacermef q, r
trabajardef f = { a, b -> b a(1) }
odef f = { a, b -> b a.curry(1)() }
paraf s, t
trabajar. Tienes que pasar todos los parámetros o decir explícitamente que estás curry. :(f x y
lo que significa lo que escribirían muchos idiomasf(x)(y)
, nof(x, y)
. ¿Quizás su código funcionaría en Groovy si escribe deq
modo que espere que se llame asíq(1)(2)
?(partial f a b ...)
: al estar acostumbrado a Haskell, extraño mucho el currículum adecuado cuando programo en otros lenguajes (aunque he estado trabajando recientemente en F # que, afortunadamente, lo admite).Hay dos puntos clave sobre la aplicación parcial. El primero es sintáctico / conveniente: algunas definiciones se vuelven más fáciles y cortas de leer y escribir, como mencionó @jk. (¡Mira la programación de Pointfree para obtener más información sobre lo increíble que es esto!)
El segundo, como mencionó @telastyn, trata sobre un modelo de funciones y no es simplemente conveniente. En la versión de Haskell, de la que obtendré mis ejemplos porque no estoy familiarizado con otros lenguajes con aplicación parcial, todas las funciones toman un solo argumento. Sí, incluso funciona como:
tomar un solo argumento; Debido a la asociatividad del constructor del tipo de función
->
, lo anterior es equivalente a:que es una función que toma
a
y devuelve una función[a] -> [a]
.Esto nos permite escribir funciones como:
que puede aplicar cualquier función a un argumento del tipo apropiado. Incluso los locos como:
Bien, ese fue un ejemplo artificial. Pero una más útil involucra la clase de tipo Aplicativo , que incluye este método:
Como puede ver, el tipo es idéntico al de
$
quitar elApplicative f
bit y, de hecho, esta clase describe la aplicación de funciones en un contexto. Entonces, en lugar de la aplicación de función normal:Podemos aplicar funciones en un contexto Aplicativo; por ejemplo, en el contexto Quizás en el que algo puede estar presente o faltante:
Ahora la parte realmente genial es que la clase de tipo Aplicativo no menciona nada sobre funciones de más de un argumento; sin embargo, puede tratar con ellas, incluso funciones de 6 argumentos como
f
:Hasta donde sé, la clase de tipo Aplicativo en su forma general no sería posible sin alguna concepción de aplicación parcial. (Para cualquier experto en programación, ¡corríjame si me equivoco!) Por supuesto, si su idioma no tiene una aplicación parcial, podría construirlo de alguna forma, pero ... simplemente no es lo mismo, ¿verdad? ? :)
fuente
Applicative
sin curry o aplicación parcial utilizaríafzip :: (f a, f b) -> f (a, b)
. En un lenguaje con funciones de orden superior, esto le permite elevar el currículum y la aplicación parcial al contexto del functor y es equivalente a(<*>)
. Sin las funciones de orden superior no tendrás,fmap
por lo que todo sería inútil.f <$> x <*> y
estilo idiomático funciona fácilmente porque el curry y la aplicación parcial funcionan fácilmente. En otras palabras, lo que es agradable es más importante que lo que es posible aquí.