¿Groovy llama aplicación parcial 'currying'?

15

Groovy tiene un concepto que llama "currying". Aquí hay un ejemplo de su wiki:

def divide = { a, b -> a / b }

def halver = divide.rcurry(2)

assert halver(8) == 4

Mi comprensión de lo que está pasando aquí es que el argumento de la mano derecha de divide está vinculado al valor 2. Esto parece ser una forma de aplicación parcial.

El término currying se usa generalmente para significar la transformación de una función que toma una serie de argumentos en una función que solo toma un argumento y devuelve otra función. Por ejemplo, aquí está el tipo de función curry en Haskell:

curry :: ((a, b) -> c) -> (a -> (b -> c))

Para las personas que no han usado Haskell a , b y c son parámetros genéricos. curry toma una función con dos argumentos, y devuelve una función que toma a y devuelve una función de b a c . He agregado un par adicional de corchetes al tipo para que esto quede más claro.

¿He malinterpretado lo que está pasando en el ejemplo genial o es simplemente una aplicación parcial con un nombre erróneo? O, de hecho, hace ambas cosas: es decir, convertir divide en una función de curry y luego aplicar parcialmente 2 a esta nueva función.

    
pregunta Richard Warburton 14.06.2012 - 11:29
fuente

4 respuestas

14

La implementación de curry de Groovy en realidad no tiene curry en ningún punto, incluso entre bastidores. Es esencialmente idéntico a la aplicación parcial.

Los métodos curry , rcurry y ncurry devuelve un objeto CurriedClosure que contiene los argumentos enlazados. También tiene un método getUncurriedArguments (con el nombre faltante: tiene funciones de curry, no argumentos) que devuelve la composición de los argumentos pasados con los argumentos enlazados.

Cuando se llama a un cierre, en última instancia llama a el% Método invokeMethod de MetaClassImpl , que verifica explícitamente si el objeto que llama es una instancia de CurriedClosure . Si es así, utiliza el mencionado getUncurriedArguments para componer la matriz completa de argumentos a aplicar:

if (objectClass == CurriedClosure.class) {
    // ...
    final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
    // [Ed: Yes, you read that right, curried = uncurried. :) ]
    // ...
    return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
}

Basándome en la confusa y algo inconsistente nomenclatura de arriba, sospecho que quienquiera que escribió esto tiene un buen entendimiento conceptual, pero quizás fue un poco apresurado y, como muchas personas inteligentes, combinó el curry con la aplicación parcial. Esto es comprensible (ver la respuesta de Paul King), aunque un poco desafortunado; Será difícil corregir esto sin romper la compatibilidad hacia atrás.

Una solución que he sugerido es sobrecargar el método curry de tal manera que cuando no se pasan argumentos, lo hace real currying y desaprueba la llamada al método con argumentos a favor de una nueva función partial . Esto podría parecer un poco extraño , pero maximizaría la compatibilidad hacia atrás, ya que no hay razón para usar una aplicación parcial sin argumentos. al mismo tiempo que evita la situación más fea (IMHO) de tener una función nueva con un nombre diferente para realizar el curry adecuado, mientras que la función realmente denominada curry hace algo diferente y confusamente similar.

No hace falta decir que el resultado de llamar a curry es completamente diferente del curry real. Si realmente se aplicara la función, podría escribir:

def add = { x, y -> x + y }
def addCurried = add.curry()   // should work like { x -> { y -> x + y } }
def add1 = addCurried(1)       // should work like { y -> 1 + y }
assert add1(1) == 2 

... y funcionaría, porque addCurried debería funcionar como { x -> { y -> x + y } } . En su lugar, lanza una excepción de tiempo de ejecución y mueres un poco dentro.

    
respondido por el Jordan Gray 14.06.2012 - 16:52
fuente
3

Creo que está claro que Groovy Curry es en realidad una aplicación parcial cuando se consideran funciones con más de dos argumentos. considerar

f :: (a,b,c) -> d

su forma al curry sería

fcurried :: a -> b -> c -> d

sin embargo, curry de groovy devolverá algo equivalente a (asumiendo que se llama con 1 argumento x)

fgroovy :: (b,c) -> d 

que llamará f con el valor de un fijo a x

es decir, mientras groovy's curry puede devolver funciones con argumentos N-1, curry tiene un solo argumento, por lo tanto groovy no puede curry con curry     

respondido por el jk. 14.06.2012 - 18:25
fuente
1

Dada esta definición encontrada en IBM:

  

El término curry está tomado de Haskell Curry, el matemático que desarrolló el concepto de funciones parciales. El curry se refiere a tomar múltiples argumentos en una función que toma muchos argumentos, lo que resulta en una nueva función que toma los argumentos restantes y devuelve un resultado.

halver es su nueva función (al curry) (o cierre), que ahora solo requiere un parámetro. Llamar a halver(10) daría como resultado 5.

Para ello, transforma una función con n argumentos en una función con n-1 argumentos. Lo mismo se dice en tu ejemplo de haskell lo que hace el curry.

    
respondido por el matcauthon 14.06.2012 - 16:43
fuente
1

Groovy tomó prestada la denominación de sus métodos de curry de muchos otros lenguajes FP no puros que también usan denominación similar para una aplicación parcial, tal vez desafortunado para dicha funcionalidad centrada en la FP. Hay varias implementaciones de curry "reales" que se proponen para su inclusión en Groovy. Un buen hilo para comenzar a leer sobre ellos está aquí:

enlace

La funcionalidad existente se mantendrá de alguna forma y la compatibilidad con versiones anteriores se tendrá en cuenta al realizar una llamada sobre los nombres de los nuevos métodos, etc., por lo que no puedo decir en esta etapa cuál es el nombre final del nuevo / Los viejos métodos serán. Probablemente haya un compromiso con el nombre, pero ya veremos.

Para la mayoría de los programadores OO, la distinción entre los dos términos (curry y aplicación parcial) es discutible en gran medida académica; sin embargo, una vez que está acostumbrado a ellos (y quienquiera que mantenga su código está capacitado para leer este estilo de codificación), la programación de estilo tácito o sin puntos (que admite el curry "real") permite que ciertos tipos de algoritmos se expresen de manera más compacta Y en algunos casos más elegantemente. Obviamente hay algo de "la belleza está en los ojos del espectador", pero tener la capacidad de soportar ambos estilos está en consonancia con la naturaleza de Groovy (OO / FP, estática / dinámica, clases / scripts, etc.).

    
respondido por el Paul King 15.06.2012 - 02:51
fuente

Lea otras preguntas en las etiquetas