-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcurry.js
78 lines (68 loc) · 2.49 KB
/
curry.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// reference: https://hackernoon.com/currying-in-js-d9ddc64f162e
// why currying: reusable first partial function call: https://javascript.info/currying-partials#currying-what-for
// run this file: node curry.js
const log1 = curry((x) => console.log(x));
const log2 = curry((x, y) => console.log(x, y));
const log3 = curry((x, y, z) => console.log(x, y, z));
log1(10); // 10
log2(10)(20); // 10 20
log3(10)(20)(30); // 10 20 30
log3(10, 20)(30); // won't run because didn't cover variadic currying
log3(10)(20, 30); // won't run because didn't cover variadic currying
// the next two curry functions cover variadic currying: (variable number of parameters)
const log4 = curryWithVariableArity((x, y, z, a) => console.log(x, y, z, a));
log4(10)(20)(30)(40); // 10 20 30 40
log4(10, 20)(30)(40); // 10 20 30 40
log4(10, 20, 30)(40); // 10 20 30 40
log4(10)(20, 30)(40); // 10 20 30 40
log4(10, 20, 30, 40); // 10 20 30 40
log4(10, 20)(30, 40); // 10 20 30 40
const log5 = curryWithBind((x, y, z, a, b) => console.log(x, y, z, a, b));
log5(10)(20)(30)(40)(50); // 10 20 30 40 50
log5(10, 20)(30)(40)(50); // 10 20 30 40 50
function curry(fn) {
if (fn.length === 0) return fn; // guard clause
const numberOfParametersNeeded = fn.length;
return nest(numberOfParametersNeeded, []);
function nest(arity, args) {
return (nextArg) => {
const atLastArgument = arity - 1 === 0;
if (atLastArgument) {
// call the original function now:
return fn(...args, nextArg);
} else {
// don't call the actual original function yet:
return nest(arity - 1, [...args, nextArg]);
}
};
}
}
function curryWithVariableArity(fn) {
// returns a variadic curried function
const numberOfParametersNeeded = fn.length;
return nest(numberOfParametersNeeded, []);
function nest(arity, args) {
return (...nextArgs) => {
const atLastArguments = arity - nextArgs.length === 0;
if (atLastArguments) {
// call the original function now:
return fn(...args, ...nextArgs);
} else {
// don't call the actual original function yet:
return nest(arity - nextArgs.length, [...args, ...nextArgs]);
}
};
}
}
function curryWithBind(fn) {
return (...args) => {
const arity = fn.length;
const atLastArguments = arity <= args.length;
if (atLastArguments) {
return fn(...args);
} else {
// pass in function with arguments partially applied already:
return curryWithBind(fn.bind(null, ...args));
}
};
}