Implementacja curryGroovy'ego nie curry w żadnym momencie, nawet za kulisami. Jest zasadniczo identyczny z częściowym zastosowaniem.
Te curry, rcurryoraz ncurrymetody zwracają CurriedClosureobiekt , który przechowuje związanego argumenty. Ma także metodę getUncurriedArguments(źle nazwaną - curry funkcje, a nie argumenty), która zwraca skład argumentów przekazanych mu wraz z powiązanymi argumentami.
Kiedy zamknięcie jest wywoływana, to ostatecznie nazywa się invokeMethodmetodęMetaClassImpl , która jawnie sprawdza, czy obiekt wywołujący jest instancją CurriedClosure. Jeśli tak, wykorzystuje powyższe getUncurriedArgumentsdo ułożenia pełnej tablicy argumentów do zastosowania:
if (objectClass == CurriedClosure.class) {
// ...
final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
// [Ed: Yes, you read that right, curried = uncurried. :) ]
// ...
return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
}
Opierając się na mylącym i nieco niekonsekwentnym nazewnictwie powyżej, podejrzewam, że ktokolwiek to napisał, ma dobre zrozumienie pojęciowe, ale być może był nieco pochopny i - jak wielu inteligentnych ludzi - splątał curry z częściowym zastosowaniem. Jest to zrozumiałe (patrz odpowiedź Paula Kinga), jeśli trochę niefortunne; trudno będzie to naprawić bez naruszenia wstecznej kompatybilności.
Jednym z rozwiązań, które zasugerowałem, jest przeciążenie currymetody w taki sposób, że gdy nie zostaną przekazane żadne argumenty, powoduje ona prawdziwe curry i przestaje być wywoływaniem metody z argumentami na korzyść nowej partialfunkcji. Może to wydawać się trochę dziwne , ale maksymalizuje zgodność wsteczną - ponieważ nie ma powodu, aby używać częściowej aplikacji z zerowymi argumentami - unikając przy tym brzydszej sytuacji (IMHO) posiadania nowej, różnie nazwanej funkcji do właściwego curry, podczas gdy funkcja faktycznie nazwany curryrobi coś innego i myląco podobnego.
Jest curryrzeczą oczywistą , że wynik wywołania jest zupełnie inny niż faktyczne curry. Jeśli funkcja naprawdę curry tę funkcję, możesz napisać:
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
… I to by działało, bo addCurriedpowinno działać jak { x -> { y -> x + y } }. Zamiast tego generuje wyjątek czasu wykonywania i umierasz trochę w środku.