chevron-left chevron-right

Function currying – o co tu chodzi i jak tego używać?

Język JavaScript jest bardzo elastycznym językiem programowania. Z racji tego, że nie jest silnie typowany oraz posiada zaimplementowany mechanizm closures, to możemy tworzyć różne ciekawie napisane funkcje, które będą bardzo elastyczne w swoim działaniu. Jednym z możliwych do osiągnięcia podejść jest podejście zwane function currying.

Czym jest currying?

Currying jest to proces rozkładania funkcji przyjmującej wiele parametrów do zestawu funkcji które przyjmują tylko jeden argument. Każda pomniejsza funkcja zwraca nową funkcję przyjmującą jeden parametr.

Opis może się wydawać trochę mglisty, więc zilustruję go prostym przykładem:

1
2
3
const add3(a, b, c) => a + b + c;
 
console.log('wynik', add3(1, 2, 3)); // 6

W tym przykładzie stworzono prostą funkcję, która przyjmuje 3 parametry: a, b, c i zwraca sumę tych parametrów (dla uproszczenia, nie sprawdzam czy wartości parametrów posiadają właściwy typ, czyli Number). Jest kilka problemów związanych z tą funkcją:

  • oczekuje zawsze 3 parametry,
  • dobrym zwyczajem jest tworzenie funkcji przyjmujących możliwe jak najmniej parametrów, aby uniknąć zbyt rozbudowanych funkcji,
  • jako że funkcja przyjmuje zawsze 3 parametry, to nie jest możliwe aby użyć tej funkcji tylko z dwoma parametrami lub z większą ilością parametrów niż 3.

Function currying w praktyce

Na szczęście, można sprawić, że funkcja dodająca kolejne liczby będzie elastyczna i pozwoli na sumowanie dowolnej liczby parametrów zawierających wartości typu Number. W tym celu powstała technika zwana currying.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function add(a) {
    let score = a;
 
    const addMore = (b) => {
        score = score + b;
 
        return addMore;
    }
 
    addMore.result = () => score;
 
    return addMore;
};
 
console.log('result:1', add(2)(4).result()); // 6
console.log('result:2', add(2)(4)(6).result()); // 12
console.log('result:3', add(2)(4)(6)(9).result()); // 21

To co się dzieje w kodzie można opisać w sposób następujący:

  1. Tworzymy funkcję add, która przyjmuje parametr a czyli jakąkolwiek liczbę (dla uproszczenia, nie ma sprawdzania typu wartości);
  2. Wartość parametru a jest zapisywana do zmiennej score, która będzie agregatorem wartości kolejnych operacji dodawania;
  3. Wewnątrz funkcji add tworzymy definicję wewnętrznej funkcji addMore, która przyjmuje parametr b. Jej rolą jest sumowanie wartości agregatora score i wartości parametru b, a na koniec zwrócenie referencji do samej siebie.
  4. Z racji tego, że w JS wszystko jest obiektem (nawet funkcje), to korzystam z właściwości języka i dodaję własność o nazwie result, której wartość jest funkcją zwracającą wartość agregatora. Tym samym, w dowolnym momencie możemy sprawdzić wynik sumowania.

Podsumowanie

W świecie JS, function currying występuje bardzo często. Jednym z najciekawszych przykładów jest sposób integracji komponentu ReactJS ze store'm w Redux, gdy używamy funkcji connect co wygląda podobnie do przykładu:

1
export default connect(mapStateToProps)(App);

Gdzie w pierwszej funkcji przyjmuje mappery jako parametr, a w drugim wywołaniu przyjmuje komponenty ReactJS jako elementy, które mają mieć powiązanie ze store'm w Redux.

Mam nadzieję, że udało mi się przedstawić temat przejrzyście. Zachęcam do komentowania 🙂