La mise en œuvre de Groovy curry
ne fait à aucun moment curry, même en coulisses. Elle est essentiellement identique à une application partielle.
Les curry
, rcurry
et les ncurry
méthodes renvoient un CurriedClosure
objet qui contient les arguments liés. Il a également une méthode getUncurriedArguments
(mal nommée - vous utilisez des fonctions, pas des arguments) qui retourne la composition des arguments qui lui sont passés avec les arguments liés.
Lorsqu'une fermeture est appelée, elle appelle finalement la invokeMethod
méthode deMetaClassImpl
, qui vérifie explicitement si l'objet appelant est une instance de CurriedClosure
. Si c'est le cas, il utilise ce qui précède getUncurriedArguments
pour composer le tableau complet des arguments à appliquer:
if (objectClass == CurriedClosure.class) {
// ...
final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
// [Ed: Yes, you read that right, curried = uncurried. :) ]
// ...
return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
}
Sur la base de la nomenclature déroutante et quelque peu incohérente ci-dessus, je soupçonne que celui qui a écrit cela a une bonne compréhension conceptuelle, mais a peut-être été un peu précipité et, comme beaucoup de gens intelligents, a confondu le curry avec une application partielle. C'est compréhensible (voir la réponse de Paul King), même si c'est un peu malheureux; il sera difficile de corriger cela sans rompre la rétrocompatibilité.
Une solution que j'ai suggérée est de surcharger la curry
méthode de telle sorte que quand aucun argument n'est passé, elle fasse un curry réel et déconseille d'appeler la méthode avec des arguments en faveur d'une nouvelle partial
fonction. Cela peut sembler un peu étrange , mais cela maximiserait la compatibilité descendante - car il n'y a aucune raison d'utiliser une application partielle avec zéro argument - tout en évitant la situation (IMHO) plus laide d'avoir une nouvelle fonction, différemment nommée, pour un curry approprié alors que la fonction en fait nommé curry
fait quelque chose de différent et de similaire.
Il va sans dire que le résultat de l'appel curry
est complètement différent du curry réel. Si cela fonctionnait vraiment, vous pourriez écrire:
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
… Et ça marcherait, parce que addCurried
ça devrait marcher comme ça { x -> { y -> x + y } }
. Au lieu de cela, il lance une exception d'exécution et vous mourez un peu à l'intérieur.