forked from indongyoo/functional-javascript-01
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fx.js
142 lines (107 loc) · 3.12 KB
/
fx.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
export const log = console.log;
export const curry = f =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
export const isIterable = a => a && a[Symbol.iterator];
export const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
const reduceF = (acc, a, f) =>
a instanceof Promise ?
a.then(a => f(acc, a), e => e == nop ? acc : Promise.reject(e)) :
f(acc, a);
export const head = iter => go1(take(1, iter), ([h]) => h);
export const reduce = curry((f, acc, iter) => {
if (!iter) return reduce(f, head(iter = acc[Symbol.iterator]()), iter);
iter = iter[Symbol.iterator]();
return go1(acc, function recur(acc) {
let cur;
while (!(cur = iter.next()).done) {
acc = reduceF(acc, cur.value, f);
if (acc instanceof Promise) return acc.then(recur);
}
return acc;
});
});
export const go = (...args) => reduce((a, f) => f(a), args);
export const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
export const take = curry((l, iter) => {
let res = [];
iter = iter[Symbol.iterator]();
return function recur() {
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
if (a instanceof Promise) {
return a
.then(a => (res.push(a), res).length == l ? res : recur())
.catch(e => e == nop ? recur() : Promise.reject(e));
}
res.push(a);
if (res.length == l) return res;
}
return res;
}();
});
export const takeAll = take(Infinity);
export const L = {};
L.range = function* (l) {
let i = -1;
while (++i < l) yield i;
};
L.map = curry(function* (f, iter) {
for (const a of iter) {
yield go1(a, f);
}
});
export const nop = Symbol('nop');
L.filter = curry(function* (f, iter) {
for (const a of iter) {
const b = go1(a, f);
if (b instanceof Promise) yield b.then(b => b ? a : Promise.reject(nop));
else if (b) yield a;
}
});
L.entries = function* (obj) {
for (const k in obj) yield [k, obj[k]];
};
L.flatten = function* (iter) {
for (const a of iter) {
if (isIterable(a) && typeof a != 'string') yield* a;
else yield a;
}
};
L.deepFlat = function* f(iter) {
for (const a of iter) {
if (isIterable(a) && typeof a != 'string') yield* f(a);
else yield a;
}
};
L.flatMap = curry(pipe(L.map, L.flatten));
export const map = curry(pipe(L.map, takeAll));
export const filter = curry(pipe(L.filter, takeAll));
export const find = curry((f, iter) => go(
iter,
L.filter(f),
take(1),
([a]) => a));
export const flatten = pipe(L.flatten, takeAll);
export const flatMap = curry(pipe(L.map, flatten));
export const add = (a, b) => a + b;
export const range = l => {
let i = -1;
let res = [];
while (++i < l) {
res.push(i);
}
return res;
};
export const C = {};
export function noop() {
}
const catchNoop = ([...arr]) =>
(arr.forEach(a => a instanceof Promise ? a.catch(noop) : a), arr);
C.reduce = curry((f, acc, iter) => iter ?
reduce(f, acc, catchNoop(iter)) :
reduce(f, catchNoop(acc)));
C.take = curry((l, iter) => take(l, catchNoop(iter)));
C.takeAll = C.take(Infinity);
C.map = curry(pipe(L.map, C.takeAll));
C.filter = curry(pipe(L.filter, C.takeAll));