Skip to content

Commit 7c8df30

Browse files
authored
Currying (#257)
1 parent 18a4ffb commit 7c8df30

File tree

1 file changed

+63
-53
lines changed

1 file changed

+63
-53
lines changed

1-js/99-js-misc/03-currying-partials/article.md

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ libs:
33

44
---
55

6-
# Currying
6+
# Каррінг
77

8-
[Currying](https://en.wikipedia.org/wiki/Currying) is an advanced technique of working with functions. It's used not only in JavaScript, but in other languages as well.
8+
[Каррінг](https://en.wikipedia.org/wiki/Currying) просунута техніка для роботи з функціями. Вона використовується не лише в JavaScript, але і в інших мовах програмування.
99

10-
Currying is a transformation of functions that translates a function from callable as `f(a, b, c)` into callable as `f(a)(b)(c)`.
10+
Каррінг — це трансформація функцій таким чином, щоб вони приймали аргументи не як `f(a, b, c)`, а як `f(a)(b)(c)`.
1111

12-
Currying doesn't call a function. It just transforms it.
12+
Каррінг не викликає функцію. Він просто трансформує її.
1313

14-
Let's see an example first, to better understand what we're talking about, and then practical applications.
14+
Давайте спочатку подивимося на приклад, щоб краще зрозуміти, про що йде мова, а потім на практичне застосування каррінгу.
1515

16-
We'll create a helper function `curry(f)` that performs currying for a two-argument `f`. In other words, `curry(f)` for two-argument `f(a, b)` translates it into a function that runs as `f(a)(b)`:
16+
Створимо допоміжну функцію `curry(f)`, яка виконує каррінг функції `f` з двома аргументами. Інакше кажучи, функція `curry(f)` трансформує `f(a, b)` в `f(a)(b)`.
1717

1818
```js run
1919
*!*
20-
function curry(f) { // curry(f) does the currying transform
20+
function curry(f) { // curry(f) виконує каррінг
2121
return function(a) {
2222
return function(b) {
2323
return f(a, b);
@@ -26,7 +26,7 @@ function curry(f) { // curry(f) does the currying transform
2626
}
2727
*/!*
2828

29-
// usage
29+
// використання
3030
function sum(a, b) {
3131
return a + b;
3232
}
@@ -36,84 +36,84 @@ let curriedSum = curry(sum);
3636
alert( curriedSum(1)(2) ); // 3
3737
```
3838

39-
As you can see, the implementation is straightforward: it's just two wrappers.
39+
Як ви бачите, реалізація доволі проста: це дві обгортки.
4040

41-
- The result of `curry(func)` is a wrapper `function(a)`.
42-
- When it is called like `curriedSum(1)`, the argument is saved in the Lexical Environment, and a new wrapper is returned `function(b)`.
43-
- Then this wrapper is called with `2` as an argument, and it passes the call to the original `sum`.
41+
- Результат `curry(func)` — обгортка `function(a)`.
42+
- Коли функція викликається як `sum(1)`, аргумент зберігається в лексичному середовищі і повертається нова обгортка `function(b)`.
43+
- Далі вже ця обгортка викликається з аргументом 2 і передає виклик до оригінальної функції `sum`.
4444

45-
More advanced implementations of currying, such as [_.curry](https://lodash.com/docs#curry) from lodash library, return a wrapper that allows a function to be called both normally and partially:
45+
Більш просунуті реалізації каррінгу, як наприклад [_.curry](https://lodash.com/docs#curry) із бібліотеки lodash, повертають обгортку, яка дозволяє запустити функцію як звичайним способом, так і частково:
4646

4747
```js run
4848
function sum(a, b) {
4949
return a + b;
5050
}
5151

52-
let curriedSum = _.curry(sum); // using _.curry from lodash library
52+
let curriedSum = _.curry(sum); // використовуємо _.curry із бібліотеки lodash
5353

54-
alert( curriedSum(1, 2) ); // 3, still callable normally
55-
alert( curriedSum(1)(2) ); // 3, called partially
54+
alert( curriedSum(1, 2) ); // 3, можна викликати як зазвичай
55+
alert( curriedSum(1)(2) ); // 3, а можна частково
5656
```
5757

58-
## Currying? What for?
58+
## Каррінг? Навіщо?
5959

60-
To understand the benefits we need a worthy real-life example.
60+
Щоб зрозуміти користь від каррінгу, нам безперечно потрібний приклад з реального життя.
6161

62-
For instance, we have the logging function `log(date, importance, message)` that formats and outputs the information. In real projects such functions have many useful features like sending logs over the network, here we'll just use `alert`:
62+
Наприклад, у нас є функція логування `log(date, importance, message)`, яка форматує і виводить інформацію. У реальних проектах у таких функцій є багато корисних можливостей, наприклад, посилати інформацію по мережі, тут для простоти використаний `alert`:
6363

6464
```js
6565
function log(date, importance, message) {
6666
alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
6767
}
6868
```
6969

70-
Let's curry it!
70+
А зараз давайте застосуємо каррінг!
7171

7272
```js
7373
log = _.curry(log);
7474
```
7575

76-
After that `log` works normally:
76+
Після цього `log` продовжує працювати нормально:
7777

7878
```js
7979
log(new Date(), "DEBUG", "some debug"); // log(a, b, c)
8080
```
8181

82-
...But also works in the curried form:
82+
...Але також працює варіант з каррінгом:
8383

8484
```js
8585
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
8686
```
8787

88-
Now we can easily make a convenience function for current logs:
88+
Давайте зробимо зручну функцію для логів з поточним часом:
8989

9090
```js
91-
// logNow will be the partial of log with fixed first argument
91+
// logNow буде частковим застосуванням функції log з фіксованим першим аргументом
9292
let logNow = log(new Date());
9393

94-
// use it
94+
// використаємо її
9595
logNow("INFO", "message"); // [HH:mm] INFO message
9696
```
9797

98-
Now `logNow` is `log` with fixed first argument, in other words "partially applied function" or "partial" for short.
98+
Тепер `logNow` - це `log` з фіксованим першим аргументом, інакше кажучи, "частково застосована" або "часткова" функція.
9999

100-
We can go further and make a convenience function for current debug logs:
100+
Ми можемо піти далі і зробити зручну функцію для саме налагоджувальних логів з поточним часом:
101101

102102
```js
103103
let debugNow = logNow("DEBUG");
104104

105105
debugNow("message"); // [HH:mm] DEBUG message
106106
```
107107

108-
So:
109-
1. We didn't lose anything after currying: `log` is still callable normally.
110-
2. We can easily generate partial functions such as for today's logs.
108+
Отже:
109+
1. Ми нічого не втратили після каррінгу: `log` все так само можна викликати нормально.
110+
2. Ми можемо легко створювати частково застосовані функції, як зробили для логів з поточним часом.
111111

112-
## Advanced curry implementation
112+
## Просунута реалізація каррінгу
113113

114-
In case you'd like to get in to the details, here's the "advanced" curry implementation for multi-argument functions that we could use above.
114+
У разі, якщо вам цікаві деталі, ось "просунута" реалізація каррінгу для функцій з множиною аргументів, яку ми могли б використати вище.
115115

116-
It's pretty short:
116+
Вона дуже коротка:
117117

118118
```js
119119
function curry(func) {
@@ -131,7 +131,7 @@ function curry(func) {
131131
}
132132
```
133133

134-
Usage examples:
134+
Приклад використання:
135135

136136
```js
137137
function sum(a, b, c) {
@@ -140,17 +140,17 @@ function sum(a, b, c) {
140140

141141
let curriedSum = curry(sum);
142142

143-
alert( curriedSum(1, 2, 3) ); // 6, still callable normally
144-
alert( curriedSum(1)(2,3) ); // 6, currying of 1st arg
145-
alert( curriedSum(1)(2)(3) ); // 6, full currying
143+
alert( curriedSum(1, 2, 3) ); // 6, все ще можна викликати нормально
144+
alert( curriedSum(1)(2,3) ); // 6, каррінг першого аргументу
145+
alert( curriedSum(1)(2)(3) ); // 6, повний каррінг
146146
```
147147

148-
The new `curry` may look complicated, but it's actually easy to understand.
148+
Нова функція `curry` виглядає складною, але насправді її легко зрозуміти.
149149

150-
The result of `curry(func)` call is the wrapper `curried` that looks like this:
150+
Результат виклику `curry(func)` - це обгортка `curried`, яка виглядає так:
151151

152152
```js
153-
// func is the function to transform
153+
// func - функція, яку ми трансформуємо
154154
function curried(...args) {
155155
if (args.length >= func.length) { // (1)
156156
return func.apply(this, args);
@@ -162,27 +162,37 @@ function curried(...args) {
162162
};
163163
```
164164

165-
When we run it, there are two `if` execution branches:
165+
Коли ми запускаємо її, є дві гілки виконання `if`:
166166

167-
1. If passed `args` count is the same or more than the original function has in its definition (`func.length`) , then just pass the call to it using `func.apply`.
168-
2. Otherwise, get a partial: we don't call `func` just yet. Instead, another wrapper is returned, that will re-apply `curried` providing previous arguments together with the new ones.
167+
1. Якщо кількість переданих `args` дорівнює або більше, ніж вказано у визначенні початковій функції `(func.length)`, то викликаємо її за допомогою `func.apply`.
168+
2. Часткове застосування: інакше `func` не викликається відразу. Замість цього, повертається інша обгортка `pass`, яка знову застосує `curried`, передавши попередні аргументи разом з новими.
169169

170-
Then, if we call it, again, we'll get either a new partial (if not enough arguments) or, finally, the result.
170+
Потім при новому виклику ми знову отримаємо або нове часткове застосування (якщо аргументів недостатньо) або, нарешті, результат.
171171

172-
```smart header="Fixed-length functions only"
173-
The currying requires the function to have a fixed number of arguments.
172+
Наприклад, давайте подивимося, що станеться у разі `sum(a, b, c)`. У неї три аргументи, так що `sum.length = 3`.
174173

175-
A function that uses rest parameters, such as `f(...args)`, can't be curried this way.
174+
Для виклику `curried(1)(2)(3)`:
175+
176+
1. Перший виклик `curried(1)` запам’ятовує `1` у своєму лексичному середовищі і повертає обгортку `pass`.
177+
2. Обгортка `pass` викликається з `(2)`: вона бере попередні аргументи (`1`), об’єднує їх з тим, що отримала сама `(2)` і викликає `curried(1, 2)` з усіма аргументами. Оскільки число аргументів все ще менше за `3`, `curry` повертає `pass`.
178+
3. Обгортка `pass` викликається знову з `(3)`. Для наступного виклику `pass(3)` бере попередні аргументи `(1, 2)` і додає до них `3`, викликаючи `curried(1, 2, 3)` - нарешті 3 аргументи, і вони передаються оригінальній функції.
179+
180+
Якщо все ще не зрозуміло, просто розпишіть послідовність викликів на папері.
181+
182+
```smart header="Тільки функції з фіксованою кількістю аргументів"
183+
Для каррінгу потрібна функція з фіксованою кількістю аргументів.
184+
185+
З функцію, яка використовує залишкові параметри, типу `f(...args)`, каррінгу не підлягає.
176186
```
177187

178-
```smart header="A little more than currying"
179-
By definition, currying should convert `sum(a, b, c)` into `sum(a)(b)(c)`.
188+
```smart header="Трохи більше, ніж каррінг"
189+
За визначенням, каррінг повинен перетворювати `sum(a, b, c)` на `sum(a)(b)(c)`.
180190
181-
But most implementations of currying in JavaScript are advanced, as described: they also keep the function callable in the multi-argument variant.
191+
Але, як було описано, більшість реалізацій каррінгу в JavaScript більш просунута: вони також залишають варіант виклику функції з декількома аргументами.
182192
```
183193

184-
## Summary
194+
## Підсумки
185195

186-
*Currying* is a transform that makes `f(a,b,c)` callable as `f(a)(b)(c)`. JavaScript implementations usually both keep the function callable normally and return the partial if the arguments count is not enough.
196+
*Каррінг* - це трансформація, яка перетворює виклик `f(a, b, c)` на `f(a)(b)(c)`. У JavaScript реалізація зазвичай дозволяє викликати функцію обома варіантами: або нормально, або повертає частково застосовану функцію, якщо недостатня кількість аргументів.
187197

188-
Currying allows us to easily get partials. As we've seen in the logging example, after currying the three argument universal function `log(date, importance, message)` gives us partials when called with one argument (like `log(date)`) or two arguments (like `log(date, importance)`).
198+
Каррінг дозволяє легко отримувати часткові функції. Як ми бачили в прикладах з логами: універсальна функція `log(date, importance, message)` після каррінгу повертає нам частково застосовану функцію, коли викликається з одним аргументом, як `log(date)` або двома аргументами, як `log(date, importance)`.

0 commit comments

Comments
 (0)