14/06/2026
W świecie programowania funkcyjnego, terminy takie jak „currying” i „częściowe stosowanie” (partial application) są często mylone i używane zamiennie. Choć są ze sobą powiązane, reprezentują odmienne koncepcje. W tym artykule zagłębimy się w działanie funkcji _.curry z biblioteki Lodash, wyjaśniając jej mechanizmy i rozwiewając wszelkie niejasności, zwłaszcza w kontekście popularnych błędów w zrozumieniu.

Zrozumienie tych subtelności jest kluczowe dla efektywnego wykorzystania potęgi programowania funkcyjnego w JavaScript. Często programiści próbują zaimplementować 'currying' na własną rękę, nieświadomie tworząc jedynie częściowe zastosowania. Lodash, dzięki swojej funkcji _.curry, dostarcza gotowe i solidne rozwiązanie, które działa dokładnie tak, jak powinno.
Czym jest Currying? Podstawy i Różnice
Zanim przejdziemy do Lodash, upewnijmy się, że rozumiemy fundamentalną różnicę między curryingiem a częściowym stosowaniem. To klucz do zrozumienia, dlaczego _.curry jest tak wyjątkowe.
Currying (Kariowanie)
Currying to technika transformacji funkcji, która przyjmuje wiele argumentów, w sekwencję funkcji, z których każda przyjmuje pojedynczy argument i zwraca kolejną funkcję, dopóki wszystkie argumenty nie zostaną dostarczone. Dopiero wtedy funkcja bazowa zostanie wykonana. Nazwa pochodzi od matematyka Hashella Curry’ego.
Wyobraź sobie funkcję dodaj(a, b, c). Funkcja curried wyglądałaby tak: dodaj(a)(b)(c). Każde wywołanie zwraca nową funkcję, która czeka na kolejny argument.
Przykładowy, uproszczony (nie Lodash) wygląd funkcji curried:
const dodaj = (a) => (b) => (c) => a + b + c;
const dodajJeden = dodaj(1);
const dodajJedenDwa = dodajJeden(2);
console.log(dodajJedenDwa(3)); // Wynik: 6Częściowe Stosowanie (Partial Application)
Częściowe stosowanie polega na stworzeniu nowej funkcji poprzez „zablokowanie” (ustalenie) jednego lub więcej argumentów funkcji pierwotnej. Nowa funkcja przyjmuje pozostałe, niezablokowane argumenty.
To jest to, co często nieświadomie implementuje się, myśląc o curryingu. Przykład:
const powitanie = (nazwa, wiadomosc) => `${wiadomosc}, ${nazwa}!`;
const powitaniePani = (nazwa) => powitanie(nazwa, 'Dzień dobry Pani');
console.log(powitaniePani('Anna')); // Wynik: Dzień dobry Pani, Anna!W tym przypadku powitaniePani jest funkcją z częściowym zastosowaniem. Zablokowaliśmy argument wiadomosc. Funkcja powitaniePani nie zwraca kolejnych funkcji dla każdego argumentu, ale od razu wykonuje powitanie po otrzymaniu wszystkich swoich argumentów.
Kluczowa Różnica
Główna różnica leży w tym, ile argumentów przyjmuje każda kolejna funkcja w łańcuchu. W curryingu każda funkcja przyjmuje dokładnie jeden argument. W częściowym stosowaniu nowa funkcja może przyjmować dowolną liczbę pozostałych argumentów.
Currying:f(a)(b)(c) – każda funkcja przyjmuje jeden argument.
Częściowe Stosowanie:f(a, b)(c) lub f(a)(b, c) – nowa funkcja może przyjmować wiele argumentów naraz.
Większość implementacji bibliotecznych (w tym Lodash) oferuje bardziej elastyczne podejście do curryingu, pozwalając na dostarczanie wielu argumentów naraz, ale zachowując mechanizm oczekiwania na pełną liczbę argumentów przed wykonaniem funkcji. To właśnie wprowadza zamieszanie.
Jak Działa Lodash _.curry?
Lodash _.curry jest niezwykle przydatnym narzędziem, które pozwala w prosty sposób tworzyć funkcje curried. Jego działanie opiera się na inteligentnym zarządzaniu liczbą oczekiwanych argumentów (arity) funkcji.
Rozważmy przykład, który często prowadzi do nieporozumień:
const myMethod = _.curry((name) => {
console.log(`Witaj ${name}!`);
});
myMethod('Wiktor'); // Wynik: Witaj Wiktor!W tym przypadku funkcja myMethod wydaje się działać natychmiast, bez zwracania kolejnej funkcji. Dlaczego? Ponieważ funkcja przekazana do _.curry, czyli (name) => { console.log(`Witaj ${name}!`); }, deklaruje, że potrzebuje tylko jednego argumentu (name). Gdy tylko dostarczymy ten argument ('Wiktor'), _.curry rozpoznaje, że wszystkie wymagane argumenty zostały spełnione i natychmiast wykonuje oryginalną funkcję. To nie jest częściowe zastosowanie, to jest pełne zastosowanie funkcji curried, która oczekiwała tylko jednego argumentu.
Lodash _.curry domyślnie określa liczbę oczekiwanych argumentów na podstawie właściwości length funkcji. W przypadku funkcji strzałkowych lub funkcji z argumentami domyślnymi, length może nie odzwierciedlać prawdziwej intencji programisty. I tu wkracza magia drugiego argumentu _.curry.
Sterowanie Liczbą Argumentów (Arity)
Co, jeśli chcemy, aby nasza funkcja curried czekała na więcej argumentów, niż wynika to z jej deklaracji? Na przykład, jeśli funkcja przyjmuje tylko jeden argument, ale chcemy, aby _.curry traktowało ją jako funkcję dwuargumentową, która ma być wywołana w dwóch krokach? Właśnie do tego służy drugi argument _.curry – liczba oczekiwanych argumentów (arity).
const myMethod = _.curry((name) => {
console.log(`Witaj ${name}!`);
}, 2); // <-- Mówimy _.curry, że ta funkcja potrzebuje dwóch argumentów
console.log('Dostarczam pierwszy argument');
const fn = myMethod('Wiktor');
console.log('Dostarczam drugi argument');
fn({}); // <-- Zwróć uwagę na argument, musimy go dostarczyć, nawet jeśli nie jest używanyW tym przykładzie dzieje się coś ciekawego. Mimo że funkcja (name) => { ... } formalnie przyjmuje tylko jeden argument, przekazaliśmy do _.curry liczbę 2. Oznacza to, że _.curry będzie czekać na dwa argumenty, zanim wykona oryginalną funkcję.
- Kiedy wywołujemy
myMethod('Wiktor'), dostarczamy tylko jeden z dwóch oczekiwanych argumentów. W rezultacie_.curryzwraca nową funkcję (nazwanąfn), która nadal czeka na brakujący argument. - Kiedy wywołujemy
fn({}), dostarczamy drugi argument (w tym przypadku pusty obiekt, ponieważ oryginalna funkcja go nie używa, ale_.curryoczekuje jego obecności). W tym momencie wszystkie oczekiwane argumenty (dwa) zostały dostarczone, więc oryginalna funkcja(name) => { console.log(`Witaj ${name}!`); }zostaje wykonana z argumentem'Wiktor', co skutkuje wyświetleniem"Witaj Wiktor!".
Ten mechanizm jest niezwykle przydatny w scenariuszach, gdzie funkcje callbackowe dostarczają dodatkowe argumenty, których Twoja funkcja nie używa, ale które są wymagane przez _.curry do pełnego wykonania. Na przykład, w obsłudze zdarzeń, gdzie callback często otrzymuje obiekt zdarzenia:
// Funkcja, która ma być częściowo zastosowana
const handleClick = (message, event) => {
console.log(`${message} - Zdarzenie:`, event.type);
};
// Kariujemy ją, mówiąc, że oczekujemy 2 argumentów
const curriedClickHandler = _.curry(handleClick, 2);
// Używamy jej w React/innych frameworkach
// const boundClickHandler = curriedClickHandler('Kliknięto przycisk');
// <button onClick={boundClickHandler}>Kliknij mnie</button>
// Symulacja wywołania przez framework (np. onClick)
const boundClickHandler = curriedClickHandler('Kliknięto przycisk');
console.log('Czekam na zdarzenie...');
boundClickHandler({ type: 'click', target: 'button' }); // Framework dostarcza obiekt zdarzenia
// Wynik: Kliknięto przycisk - Zdarzenie: clickW tym przykładzie curriedClickHandler('Kliknięto przycisk') dostarcza tylko pierwszy argument. Ponieważ powiedzieliśmy _.curry, że oczekujemy dwóch argumentów, zwraca on nową funkcję, która czeka na drugi argument (obiekt zdarzenia). Kiedy framework wywołuje tę nową funkcję, dostarczając obiekt zdarzenia, funkcja handleClick zostaje wreszcie wykonana.
Korzyści z Używania Curryingu
Currying, szczególnie w połączeniu z Lodash _.curry, oferuje szereg korzyści w programowaniu funkcyjnym:
- Większa Reużywalność Kodu: Możesz łatwo tworzyć bardziej specyficzne funkcje z ogólnych funkcji, częściowo je stosując.
- Lepsza Kompozycja Funkcji: Funkcje curried są idealne do łączenia w łańcuchy (function composition), ponieważ każda z nich przyjmuje i zwraca pojedynczą wartość (lub funkcję oczekującą na kolejny argument).
- Czytelność i Elastyczność: Kod staje się bardziej deklaratywny i łatwiejszy do zrozumienia, gdy operacje są wykonywane krok po kroku.
- Unikanie Błędów: Dzięki temu, że funkcja nie zostanie wykonana, dopóki nie otrzyma wszystkich argumentów, można uniknąć niezamierzonych wywołań.
Porównanie Curryingu i Częściowego Stosowania
| Cecha | Currying (Kariowanie) | Częściowe Stosowanie |
|---|---|---|
| Liczba argumentów na krok | Zawsze jeden | Jeden lub więcej |
| Cel | Transformacja funkcji wieloargumentowej w sekwencję funkcji jednoargumentowych. | Tworzenie nowej funkcji z mniejszą liczbą argumentów poprzez zablokowanie niektórych. |
| Wywołanie | f(a)(b)(c) | f(a, b)(c) lub f(a)(b, c) |
| Narzędzia | _.curry (Lodash) | _.partial (Lodash), .bind() (JS), closures |
| Kiedy używać? | Gdy potrzebujesz elastyczności w dostarczaniu argumentów po kolei, kompozycji funkcji. | Gdy chcesz stworzyć bardziej wyspecjalizowaną funkcję z predefiniowanymi argumentami. |
Często Zadawane Pytania (FAQ)
Czy mogę używać _.curry z funkcjami, które mają argumenty opcjonalne lub rest parameters?
Tak, ale musisz być świadomy, jak _.curry określa arity (liczbę oczekiwanych argumentów). Jeśli Twoja funkcja ma argumenty opcjonalne lub rest parameters (...args), jej właściwość length może nie odzwierciedlać prawdziwej liczby argumentów, na które chcesz poczekać. W takich przypadkach zawsze należy jawnie podać drugi argument do _.curry, określający dokładną liczbę argumentów, które mają zostać dostarczone przed wykonaniem funkcji.
// Funkcja z rest parameters
const sumAll = (a, b, ...rest) => a + b + rest.reduce((acc, val) => acc + val, 0);
// _.curry(sumAll) domyślnie przyjmie arity 2 (a, b)
const curriedSum1 = _.curry(sumAll);
console.log(curriedSum1(1)(2)(3, 4)); // Wynik: 10 (3,4 są przekazane do rest)
// Jeśli chcesz, aby czekało na 4 argumenty
const curriedSum2 = _.curry(sumAll, 4);
console.log(curriedSum2(1)(2)(3)(4)); // Wynik: 10Czy _.curry obsługuje argumenty placeholderowe?
Tak, Lodash _.curry obsługuje argumenty placeholderowe, takie jak _ (underscore), co pozwala na częściowe zastosowanie argumentów w dowolnej pozycji. Jest to szczególnie przydatne, gdy chcesz zablokować argumenty w środku listy argumentów funkcji.
const greet = (greeting, name, punctuation) => `${greeting}, ${name}${punctuation}`;
const curriedGreet = _.curry(greet);
// Zablokuj drugi argument (name), pozostawiając resztę do uzupełnienia
const helloSomeone = curriedGreet('Cześć', _, '!'); // _ to placeholder
console.log(helloSomeone('Anna')); // Wynik: Cześć, Anna!Czy mogę „odkariować” funkcję?
Technicznie rzecz biorąc, „odkariowanie” funkcji (uncurrying) oznacza przekształcenie funkcji curried z powrotem w funkcję przyjmującą wszystkie argumenty naraz. Lodash nie ma bezpośredniej funkcji _.uncurry, ale można to osiągnąć, choć rzadko jest to potrzebne w praktyce. Zazwyczaj, jeśli funkcja została skariowana, zamierzamy ją używać w tej formie.
Jakie są alternatywy dla Lodash _.curry?
Choć Lodash jest popularnym wyborem, istnieją inne biblioteki, takie jak Ramda, która jest zaprojektowana z myślą o programowaniu funkcyjnym i oferuje funkcję curry jako jedną ze swoich podstawowych cech. Można również zaimplementować własną prostą funkcję curryingu, choć wersja Lodash jest zoptymalizowana i radzi sobie z wieloma przypadkami brzegowymi.
Podsumowanie
Zrozumienie działania _.curry z Lodash jest kluczowe dla efektywnego programowania funkcyjnego w JavaScript. Pamiętaj, że _.curry domyślnie określa arity funkcji na podstawie jej właściwości length. Jeśli chcesz, aby twoja funkcja curried czekała na konkretną liczbę argumentów, niezależnie od deklaracji funkcji bazowej, zawsze podawaj jawnie drugi argument do _.curry. Dzięki temu będziesz mógł w pełni wykorzystać potencjał curryingu, tworząc bardziej modułowy, elastyczny i czytelny kod. Praktyka czyni mistrza, więc eksperymentuj z przykładami i zobacz, jak currying może uprościć Twoje rozwiązania.
Zainteresował Cię artykuł Lodash _.curry: Zrozumieć Funkcje Curried? Zajrzyj też do kategorii Kulinaria, znajdziesz tam więcej podobnych treści!
