Trafiłem wczoraj na ciekawy przypadek: EventMachine udostępnia metodę add_timer, która oczekuje w bloku dostać kod callbacka. Niestety nie pozwala na przekazywanie dodatkowych argumentów do tego bloku, no ale w końcu to Ruby – można skorzystać z domknięcia. Potrzebowałem w pętli ustawić tych timerów całą masę, każdy z innymi parametrami i uznałem, że to idealne zastosowanie dla Proc#curry – dla każdego argumentu stworzę sobie proca z odpowiednim argumentem w domknięciu. Okazało się jednak, że #curry w Ruby ma pewną cechę – jeżeli zcurryowaną metodę wywoła się z ostatnim argumentem, to zostaje ona automatycznie wykonana i nie zwraca już proca…
Naprędce napisałem własne curry, które działa nieco inaczej:
def bcurry(f, *args)
proc{|*nargs| f[*args, *nargs]}
end
W ten sposób udało mi się problem rozwiązać, ale nie wydaje mi się to szczególnie zgrabne. Macie jakieś propozycje na rozwiązanie wykorzystujące metody biblioteczne? Czy ktoś potrafi wyjaśnić dlaczego oryginalne #curry zachowuje się tak a nie inaczej? Z pewnością to jest “fancy”, ale czy znajduje praktyczne zastosowania?
Oryginalnie tak się zachowuje ze względu że taka jest definicja partial application. Teoretycznie metoda której zaaplikowano ostatni argument powinna zostac wykonana. Ja bym to rozwiązał najbardziej lamerskim sposobem czyli dodał argument execute=nil na koniec metody i użył bibliotecznego curry
W ten sposób po ostatnim “twoim” curry EM dostałby proca z jednym argumentem ‘execute’ którego wartość jest dowolna dla ciebie.
Hmm, ale ja nie wołam samodzielnie tej metody – robi to EM, więc jeżeli ona będzie przyjmować dwa argumenty to po zcurryowaniu i wywołaniu z pierwszym dostanę proca na którym bezargumentowy call zwróci kopię tego samego proca.
Nie zrozumiałeś - różnica pomiiędzy partial application a currying jest tutaj niestotana. Obydwa sposoby standardowo po zaaplikoaniu ostatniego argumentu zwracają wartość nie funkcję bez argumentów.
Chodzi mi o to że chcesz dać callbacka: który zrobi puts x gdzie x to jest twoja wartość z pętli.
Z definicji, którą wziąłem z Wikipedii (wiem, że to nie jest jakieś fantastyczne źródło, ale zawsze) rozumiem, że papply zawsze zwraca funkcję tego samego rzędu, ale powiedzmy, że to nieistotne – tak czy siak nie ma bibliotecznego papply w Ruby.
[quote=Świstak]cos = lambda{|x,y,z,execute| coś z tym zrobisz }
i nagle buja[/quote]
Niestety nie buja. Napisałem to w poprzednim poście
Problem z Twoją propozycją jest taki, że jeżeli zcurryowana lambda oczekuje jednego dodatkowego argumentu, to EM.timer wołając na niej bezargumentowy call zwróci identyczną kopię tego proca, oczejującą tego nieszczęsnego ostatniego argumentu – właśnie dlatego, że na oryginalnej lambdzie zawołałeś curry.
Chyba Cię po prostu źle zrozumiałem - myślałem, że chodzi Ci o jakieś funkcjonalności w Rubym, które są w JS, a Tobie, jak rozumiem, chodziło o artykuł Tak czy siak, coś tam wyskrobałem - przydałoby się to jednak okrasić jakimiś bardziej życiowymi przykładami.
[quote]…
Proc#curry is like an easter egg. Matz felt no need to care about the
interaction between Proc#curry and other advanced features.
…[/quote]
To wiele tłumaczy Ja w ciągle w szoku…
Problem udało się rozwiązać poprzez zamianę pętli while na samodzielnie zdefiniowanego eacha – dzięki temu każda instancja proca zawierającego instrukcje dla schedulowanego timera miała własne domknięcie zawierające potrzebne dane. W tym wypadku curry czy partial application okazały się zbędne. Jak się nad tym dobrze zastanowić, to sytuacja, w której korzysta się z metod iterowania innych niż poprzez metodę przyjmującą blok należą do rzadkości, i praktycznie zawsze można ich uniknąć.
Takie podejście faktycznie sprawia, że curry i papply mogą być w świecie Ruby bardziej ciekawostkami niż niezbędnymi tworami.