Currying
Programowanie funkcyjne jest prawie tak samo popularne jak programowanie obiektowe. Wiele koncepcji z programowania obiektowego tak mocno przeniknęło do programowania w ogóle, że czasami nawet nie dostrzegamy pochodzenia danego podejścia. Programowanie funkcyjne również ma swoje ciekawe koncepcje - jedną z nich jest currying. W tym artykule staram się na przykładach pokazać jak działa currying oraz jakie problemy pozwala rozwiązać.
Z tego artykułu dowiesz się:
Co to jest currying?
Jak currying pomoże rozwiązać problem ze zbyt dużą liczbą argumentów w funkcji?
Gdzie możemy się spotkać z currying'iem w projektach z React?
Jak w Javascript działa konwertowanie do wartości prymitywnej?
Spotkaliście się kiedyś z problemem za dużej liczby argumentów w jednej funkcji? Takie funkcje stają się nieczytelne, a ich złożoność może prowadzić do trudności w samym procesie wytwarzania kodu i co za tym idzie - błędów. W wielu poradnikach i zestawieniach dobrych praktyk bardzo często wskazuje się, że funkcje nie powinny przyjmować więcej niż 1 lub 2 argumenty.
Dzisiaj chciałbym poruszyć ciekawy temat związany z programowaniem funkcyjnym, tj. currying. Na currying często mówi się, że jest to rozwijanie funkcji - wprowadza on możliwość elastycznego rozwijania funkcji przy zachowanie możliwie małej ilość przekazywanych argumentów.
Jedna funkcja, wiele argumentów
Najprostszym i najczęściej spotykanym przykładem, gdzie currying mógłby nam się przydać jest funkcja sumująca liczby.
Zwróćcie uwagę, że tak przygotowana funkcja jest mało elastyczna i zawsze musi przyjąć trzy argumenty. W przeciwnym wypadku zwróci nam błąd lub niepoprawny wynik.
Oczywiście możemy napisać tę funkcję w taki sposób, aby ilość argumentów nie miała znaczenia i funkcja nadal zwracała poprawny wynik, np:
Czy takie rozwiązanie jest w porządku? W pewnym sensie tak, ale duża ilość argumentów może w pewnym momencie spowodować, że nasz kod stanie się mało czytelny.
Jedna funkcja, jeden argument
Założenia currying’u są bardzo proste: funkcja przyjmuje minimalną ilość argumentów i jest na tyle elastyczna, że kolejne jej wywołania pozwolą nam otrzymać prawidłowy wynik. Wywołanie takie funkcji mogłoby wyglądać następująco:
W powyższym przykładzie mogę podawać kolejne argumenty do funkcji sum
poprzez jej kolejne wywołania. Co się dzieje pod spodem? Funkcja sum
zwraca funkcję pośrednią, która przyjmuje jeden argument i którą możemy wywołać ponownie. Zgodnie z definicją currying to przekształcenie jednej funkcji w sekwencję funkcji, do których przekazujemy po jednym argumencie. Napiszmy zatem funkcję sumującą, która przyjmie jeden argument i zwróci nową funkcję zwiększającą przekazaną liczbę przy kolejnym wywołaniu.
Powyższy przykład ilustruje zwrócenie przez naszą funkcję sum
wewnętrznej funkcji add
. Funkcja sum
przyjmuje jeden argument, podobnie zresztą jak funkcja wewnętrzna. Na każdym poziomie naszej funkcji zwracamy oczywiście funkcję wewnętrzną, bo to ona odpowiada za nasze obliczenia. Na tym etapie nasz kod jeszcze nie działa poprawnie. Wywołanie metody sum
spowoduje, że zwrócimy funkcję, a nie jej wynik.
Aby zwrócić wartość z naszych obliczeń możemy dopisać wewnętrzną metodę getValue
. Takie działanie będzie jak najbardziej poprawne, ponieważ w Javascript wszystko jest obiektem - funkcja również.
Jak widać na zaprezentowanym przykładzie osiągnęliśmy zamierzony efekt, ale pobranie wartości wymaga dodatkowego wywołania. Pojawia się zatem pytanie czy jesteśmy w stanie pominąć ostatni krok, tak aby ostatnie wywołanie od razu zwracało nam końcową wartość.
Konwertowanie do wartości prymitywnej
Javascript posiada wbudowaną metodę valueOf
, która wywoływana jest automatycznie by przekonwertować obiekt do wartości prymitywnej. Warto zwrócić uwagę, że konwertowanie odbywa się przy użyciu innej wbudowanej metody toString
. Mając tę wiedzę, możemy spokojnie założyć, że nadpisanie obu tych metod pozwoli nam zmienić wartość ostatecznie zwracaną. Jak mogłoby to wyglądać?
Metoda valueOf
jest wywoływana przez interpreter Javascriptu, dlatego nie ma potrzeby, żebyśmy wywoływali ją samodzielnie. Pamiętajmy, że tak nadpisana funkcja nie powinna przyjmować argumentów.
Podsumowanie
Osiągnęliśmy nasz cel. Powyższe przykłady ilustrują jak można wykorzystać currying. Pytanie zatem czy warto? Wydaje mi się, że stosowanie tej techniki pozwala nam uelastycznić nasze funkcje i zwiększyć czytelność naszego kodu. Przykładów stosowania currying’u w codziennej pracy można znaleźć sporo - wystarczy zwrócić chociażby uwagę na React Redux. Metoda connect
to dobry przykład zastosowania powyższych zasad:
Komentarze (0)
Jeszcze nikt nic nie napisał, ale to znaczy że... możesz być pierwszy/pierwsza.