La mise en œuvre de Groovy curryne fait à aucun moment curry, même en coulisses. Elle est essentiellement identique à une application partielle.
Les curry, rcurryet les ncurryméthodes renvoient un CurriedClosureobjet 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 invokeMethodmé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 getUncurriedArgumentspour 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 currymé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 partialfonction. 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é curryfait quelque chose de différent et de similaire.
Il va sans dire que le résultat de l'appel curryest 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.