├── .idea
├── .gitignore
├── codeStyles
│ └── codeStyleConfig.xml
├── functional-javascript-01.iml
├── inspectionProfiles
│ └── Project_Default.xml
├── modules.xml
└── vcs.xml
├── 01. 함수형 자바스크립트 기본기
├── 1.html
└── 2.html
├── 02. ES6에서의 순회와 이터러블 이터레이터 프로토콜
└── index.html
├── 03. 제너레이터와 이터레이터
└── index.html
├── 04. map, filter, reduce
├── 1
│ └── 1. index.html
└── 2
│ ├── html
│ └── 2.html
│ └── lib
│ └── fx.js
├── 05. 코드를 값으로 다루어 표현력 높이기
└── 1
│ ├── html
│ └── 1.html
│ └── lib
│ └── fx.js
├── 06. HTML 출력해보기
└── 1
│ ├── html
│ └── 1.html
│ └── lib
│ └── fx.js
├── 07. 지연성 1, 8. 지연성 2
├── 1
│ ├── html
│ │ └── 1.html
│ └── lib
│ │ └── fx.js
├── 2
│ ├── html
│ │ └── 2.html
│ └── lib
│ │ └── fx.js
├── 3
│ ├── html
│ │ └── 1.html
│ └── lib
│ │ └── fx.js
└── 4
│ ├── html
│ ├── 1.html
│ └── yield...md
│ └── lib
│ └── fx.js
├── 09. 비동기 동시성 프로그래밍
└── 1
│ ├── html
│ ├── 1-1.html
│ ├── 1-2.html
│ └── 1-3.html
│ └── lib
│ └── fx.js
├── 10. 비동기 동시성 프로그래밍 2
└── 1
│ ├── html
│ ├── 1-1.html
│ └── 1-2.html
│ └── lib
│ └── fx.js
├── 11. 비동기 동시성 프로그래밍 3
└── 1
│ ├── html
│ └── 1-1.html
│ └── lib
│ └── fx.js
├── README.md
├── fx.js
├── index.js
└── test
└── index.js
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # 디폴트 무시된 파일
2 | /shelf/
3 | /workspace.xml
4 | # 에디터 기반 HTTP 클라이언트 요청
5 | /httpRequests/
6 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/functional-javascript-01.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/01. 함수형 자바스크립트 기본기/1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 평가
4 | - 코드가 계산(Evaluation) 되어 값을 만드는 것
5 |
6 | # 일급
7 | - 값으로 다룰 수 있다.
8 | - 변수에 담을 수 있다.
9 | - 함수의 인자로 사용될 수 있다.
10 | - 함수의 결과로 사용될 수 있다.
11 |
12 |
18 |
19 | # 일급 함수
20 | - 함수를 값으로 다룰 수 있다.
21 | - 조합성과 추상화의 도구
22 |
23 |
39 |
--------------------------------------------------------------------------------
/01. 함수형 자바스크립트 기본기/2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 일급 함수
4 | - 함수가 값으로 다뤄질 수 있다.
5 |
6 | # 고차 함수
7 | - 함수를 값으로 다루는 함수
8 |
9 | ## 함수를 인자로 받아서 실행하는 함수
10 | - apply1
11 | - times
12 |
13 |
42 |
43 | ## 함수를 만들어 리턴하는 함수 (클로저를 만들어 리턴하는 함수)
44 | - addMaker
45 |
46 |
63 |
--------------------------------------------------------------------------------
/02. ES6에서의 순회와 이터러블 이터레이터 프로토콜/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## 기존과 달라진 ES6에서의 리스트 순회
4 | - for i++
5 | - for of
6 |
7 |
23 |
24 | ### Array를 통해 알아보기
25 |
26 |
42 |
43 | ### Set을 통해 알아보기
44 |
45 |
55 |
56 | ### Map을 통해 알아보기
57 |
58 |
66 |
67 | ## 이터러블/이터레이터 프로토콜
68 | - 이터러블: 이터레이터를 리턴하는 [Symbol.iterator]() 를 가진 값
69 | - 이터레이터: { value, done } 객체를 리턴하는 next() 를 가진 값
70 | - 이터러블/이터레이터 프로토콜: 이터러블을 for...of, 전개 연산자 등과 함께 동작하도록한 규약
71 |
72 | ### 사용자 정의 이터러블을 통해 알아보기
73 |
74 |
110 |
111 | ## 전개 연산자
112 |
113 |
119 |
--------------------------------------------------------------------------------
/03. 제너레이터와 이터레이터/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 제너레이터/이터레이터
4 | - 제너레이터: 이터레이터이자 이터러블을 생성하는 함수
5 |
6 | 제너레이터를 통해 어떤 값도 순회하게 만들 수 있음
7 |
8 |
31 |
32 | # odds
33 |
34 |
85 |
86 | # for of, 전개 연산자, 구조 분해, 나머지 연산자
87 |
88 |
103 |
--------------------------------------------------------------------------------
/04. map, filter, reduce/1/1. index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 | # map
15 |
16 |
42 |
43 | # 이터러블 프로토콜을 따른 map의 다형성
44 |
45 |
81 |
82 |
83 | # filter
84 |
85 |
122 |
123 | # reduce
124 |
125 |
159 |
160 |
171 |
--------------------------------------------------------------------------------
/04. map, filter, reduce/2/html/2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
--------------------------------------------------------------------------------
/04. map, filter, reduce/2/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const map = (f, iter) => {
4 | let res = [];
5 | for (const a of iter) {
6 | res.push(f(a));
7 | }
8 | return res;
9 | };
10 |
11 | const filter = (f, iter) => {
12 | let res = [];
13 | for (const a of iter) {
14 | if (f(a)) res.push(a);
15 | }
16 | return res;
17 | };
18 |
19 | const reduce = (f, acc, iter) => {
20 | if (!iter) {
21 | iter = acc[Symbol.iterator]();
22 | acc = iter.next().value;
23 | }
24 | for (const a of iter) {
25 | acc = f(acc, a);
26 | }
27 | return acc;
28 | };
29 |
--------------------------------------------------------------------------------
/05. 코드를 값으로 다루어 표현력 높이기/1/html/1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
22 | # 코드를 값으로 다루어 표현력 높이기
23 |
24 | ## go, pipe
25 |
26 |
49 |
50 |
85 |
86 | ## curry
87 |
88 |
102 |
103 | # 함수 조합으로 함수 만들기
104 |
105 |
140 |
--------------------------------------------------------------------------------
/05. 코드를 값으로 다루어 표현력 높이기/1/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 | /**
3 | * 함수를 받아서 함수를 return
4 | * 인자가 2개 이상이면 받아둔 함수를 즉시 실행하고 적다면 return한 이후에 인자를 합쳐서 실행
5 | *
6 | * 1. curry는 f라는 함수를 인자로 받아 새로운 함수를 반환하는 화살표 함수
7 | * 2. 첫번째 반환 함수: (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
8 | * 3. 인자 검사 및 호춣
9 | * 3-1. _.length가 참이면 f(a, ..._)를 호출해 즉시 결과를 반환
10 | * 3-2. 거짓이면 다시 curry(f)를 호출해 나머지 인자를 받아 f(a, ..._)를 호출
11 | */
12 |
13 | const curry = f =>
14 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
15 |
16 | const map = curry((f, iter) => {
17 | let res = [];
18 | for (const a of iter) {
19 | res.push(f(a));
20 | }
21 | return res;
22 | });
23 |
24 | const filter = curry((f, iter) => {
25 | let res = [];
26 | for (const a of iter) {
27 | if (f(a)) res.push(a);
28 | }
29 | return res;
30 | });
31 |
32 | const reduce = curry((f, acc, iter) => {
33 | if (!iter) {
34 | iter = acc[Symbol.iterator]();
35 | acc = iter.next().value;
36 | }
37 | for (const a of iter) {
38 | acc = f(acc, a);
39 | }
40 | return acc;
41 | });
42 |
--------------------------------------------------------------------------------
/06. HTML 출력해보기/1/html/1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML 출력해보기 - 장바구니
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/06. HTML 출력해보기/1/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | const map = curry((f, iter) => {
7 | let res = [];
8 | for (const a of iter) {
9 | res.push(f(a));
10 | }
11 | return res;
12 | });
13 |
14 | const filter = curry((f, iter) => {
15 | let res = [];
16 | for (const a of iter) {
17 | if (f(a)) res.push(a);
18 | }
19 | return res;
20 | });
21 |
22 | const reduce = curry((f, acc, iter) => {
23 | if (!iter) {
24 | iter = acc[Symbol.iterator]();
25 | acc = iter.next().value;
26 | }
27 | for (const a of iter) {
28 | acc = f(acc, a);
29 | }
30 | return acc;
31 | });
32 |
33 | const go = (...args) => reduce((a, f) => f(a), args);
34 |
35 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
36 |
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/1/html/1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## range
4 |
5 |
21 |
22 | ## 느긋한 L.range
23 |
24 |
37 |
38 |
49 |
50 | ## take
51 |
52 |
77 |
78 |
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/1/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | const map = curry((f, iter) => {
7 | let res = [];
8 | for (const a of iter) {
9 | res.push(f(a));
10 | }
11 | return res;
12 | });
13 |
14 | const filter = curry((f, iter) => {
15 | let res = [];
16 | for (const a of iter) {
17 | if (f(a)) res.push(a);
18 | }
19 | return res;
20 | });
21 |
22 | const reduce = curry((f, acc, iter) => {
23 | if (!iter) {
24 | iter = acc[Symbol.iterator]();
25 | acc = iter.next().value;
26 | }
27 | for (const a of iter) {
28 | acc = f(acc, a);
29 | }
30 | return acc;
31 | });
32 |
33 | const go = (...args) => reduce((a, f) => f(a), args);
34 |
35 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/2/html/2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 이터러블 중심 프로그래밍에서의 지연 평가 (Lazy Evaluation)
4 | - 제때 계산법
5 | - 느긋한 계산법
6 | - 제너레이터/이터레이터 프로토콜을 기반으로 구현
7 |
8 | ### L.map
9 |
10 |
19 |
20 | ### L.filter
21 |
22 |
31 |
32 |
33 | ### range, map, filter, take, reduce 중첩 사용
34 |
35 |
102 |
103 | ### L.range, L.map, L.filter, take, reduce 중첩 사용
104 |
105 |
151 |
152 | ### map, filter 계열 함수들이 가지는 결합 법칙
153 |
154 | - 사용하는 데이터가 무엇이든지
155 | - 사용하는 보조 함수가 순수 함수라면 무엇이든지
156 | - 아래와 같이 결합한다면 둘 다 결과가 같다.
157 |
158 | [[mapping, mapping], [filtering, filtering], [mapping, mapping]]
159 | =
160 | [[mapping, filtering, mapping], [mapping, filtering, mapping]]
161 |
162 |
163 | ## 결과를 만드는 함수 reduce, take
164 |
165 | ### reduce
166 |
167 |
191 |
192 | ### take, find
193 |
194 |
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/2/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | // const map = curry((f, iter) => {
7 | // let res = [];
8 | // for (const a of iter) {
9 | // res.push(f(a));
10 | // }
11 | // return res;
12 | // });
13 | //
14 | // const filter = curry((f, iter) => {
15 | // let res = [];
16 | // for (const a of iter) {
17 | // if (f(a)) res.push(a);
18 | // }
19 | // return res;
20 | // });
21 |
22 | // const reduce = curry((f, acc, iter) => {
23 | // if (!iter) {
24 | // iter = acc[Symbol.iterator]();
25 | // acc = iter.next().value;
26 | // }
27 | // for (const a of iter) {
28 | // acc = f(acc, a);
29 | // }
30 | // return acc;
31 | // });
32 |
33 | const go = (...args) => reduce((a, f) => f(a), args);
34 |
35 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
36 |
37 | var add = (a, b) => a + b;
38 |
39 | // const range = l => {
40 | // let i = -1;
41 | // let res = [];
42 | // while (++i < l) {
43 | // res.push(i);
44 | // }
45 | // return res;
46 | // };
47 |
48 | const L = {};
49 |
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/3/html/1.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/3/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | const L = {};
7 |
8 | L.range = function* (l) {
9 | let i = -1;
10 | while (++i < l) {
11 | yield i;
12 | }
13 | };
14 |
15 | L.entries = function* (obj) {
16 | for (const k in obj) yield [k, obj[k]];
17 | };
18 |
19 | const take = curry((l, iter) => {
20 | let res = [];
21 | iter = iter[Symbol.iterator]();
22 | let cur;
23 | while (!(cur = iter.next()).done) {
24 | const a = cur.value;
25 | res.push(a);
26 | if (res.length == l) return res;
27 | }
28 | return res;
29 | });
30 |
31 | const reduce = curry((f, acc, iter) => {
32 | if (!iter) {
33 | iter = acc[Symbol.iterator]();
34 | acc = iter.next().value;
35 | } else {
36 | iter = iter[Symbol.iterator]();
37 | }
38 | let cur;
39 | while (!(cur = iter.next()).done) {
40 | const a = cur.value;
41 | acc = f(acc, a);
42 | }
43 | return acc;
44 | });
45 |
46 | const find = curry((f, iter) => go(
47 | iter,
48 | L.filter(f),
49 | take(1),
50 | ([a]) => a));
51 |
52 | const go = (...args) => reduce((a, f) => f(a), args);
53 |
54 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
55 |
56 | var add = (a, b) => a + b;
57 |
58 | const range = l => {
59 | let i = -1;
60 | let res = [];
61 | while (++i < l) {
62 | res.push(i);
63 | }
64 | return res;
65 | };
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/4/html/1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## L.flatten
4 |
5 |
33 |
34 | ## yield *
35 |
36 |
44 |
45 | `yield *`을 활용하면 위 코드를 아래와 같이 변경할 수 있습니다. `yield *iterable`은 `for (const val of iterable) yield val;` 과 같습니다.
46 |
47 |
55 |
56 | ## L.deepFlat
57 |
58 | 만일 깊은 Iterable을 모두 펼치고 싶다면 아래와 같이 `L.deepFlat`을 구현하여 사용할 수 있습니다. `L.deepFlat`은 깊은 Iterable을 펼쳐줍니다.
59 |
60 |
70 |
71 | ## L.flatMap
72 |
73 |
105 |
106 |
107 | ## 2차원 배열 다루기
108 |
109 |
125 |
126 |
127 | ## 지연성 / 이터러블 중심 프로그래밍 실무적인 코드
128 |
129 |
164 |
165 |
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/4/html/yield...md:
--------------------------------------------------------------------------------
1 | ## yield*
2 |
3 | ```javascript
4 | L.flatten = function* (iter) {
5 | for (const a of iter) {
6 | if (isIterable(a)) for (const b of a) yield b
7 | else yield a;
8 | }
9 | };
10 | ```
11 |
12 | `yield*`을 활용하면 위 코드를 아래와 같이 변경할 수 있습니다. `yield* iterable`은 `for (const val of iterable) yield val;` 과 같습니다.
13 |
14 | ```javascript
15 | L.flatten = function* (iter) {
16 | for (const a of iter) {
17 | if (isIterable(a)) yield* a;
18 | else yield a;
19 | }
20 | };
21 | ```
22 |
23 | ## L.deepFlat
24 |
25 | 만일 깊은 Iterable을 모두 펼치고 싶다면 아래와 같이 `L.deepFlat`을 구현하여 사용할 수 있습니다. `L.deepFlat`은 깊은 Iterable을 펼쳐줍니다.
26 |
27 | ```javascript
28 | L.deepFlat = function* f(iter) {
29 | for (const a of iter) {
30 | if (isIterable(a)) yield* f(a);
31 | else yield a;
32 | }
33 | };
34 | log([...L.deepFlat([1, [2, [3, 4], [[5]]]])]);
35 | // [1, 2, 3, 4, 5];
36 | ```
--------------------------------------------------------------------------------
/07. 지연성 1, 8. 지연성 2/4/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | const reduce = curry((f, acc, iter) => {
7 | if (!iter) {
8 | iter = acc[Symbol.iterator]();
9 | acc = iter.next().value;
10 | } else {
11 | iter = iter[Symbol.iterator]();
12 | }
13 | let cur;
14 | while (!(cur = iter.next()).done) {
15 | const a = cur.value;
16 | acc = f(acc, a);
17 | }
18 | return acc;
19 | });
20 |
21 | const go = (...args) => reduce((a, f) => f(a), args);
22 |
23 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
24 |
25 | const take = curry((l, iter) => {
26 | let res = [];
27 | iter = iter[Symbol.iterator]();
28 | let cur;
29 | while (!(cur = iter.next()).done) {
30 | const a = cur.value;
31 | res.push(a);
32 | if (res.length == l) return res;
33 | }
34 | return res;
35 | });
36 |
37 | const takeAll = take(Infinity);
38 |
39 | const L = {};
40 |
41 | L.range = function* (l) {
42 | let i = -1;
43 | while (++i < l) yield i;
44 | };
45 |
46 | L.map = curry(function* (f, iter) {
47 | for (const a of iter) {
48 | yield f(a);
49 | }
50 | });
51 |
52 | L.filter = curry(function* (f, iter) {
53 | for (const a of iter) {
54 | if (f(a)) yield a;
55 | }
56 | });
57 |
58 | L.entries = function* (obj) {
59 | for (const k in obj) yield [k, obj[k]];
60 | };
61 |
62 | const map = curry(pipe(L.map, takeAll));
63 |
64 | const filter = curry(pipe(L.filter, takeAll));
65 |
66 | const find = curry((f, iter) => go(
67 | iter,
68 | L.filter(f),
69 | take(1),
70 | ([a]) => a));
71 |
72 | var add = (a, b) => a + b;
73 |
74 | const range = l => {
75 | let i = -1;
76 | let res = [];
77 | while (++i < l) {
78 | res.push(i);
79 | }
80 | return res;
81 | };
--------------------------------------------------------------------------------
/09. 비동기 동시성 프로그래밍/1/html/1-1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Promise
4 |
5 | ## 일급
6 |
7 |
33 |
34 | ## 일급 활용
35 |
36 |
49 |
50 | ## Composition
51 |
52 |
70 |
71 | ## Kleisli Composition
72 |
73 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/09. 비동기 동시성 프로그래밍/1/html/1-2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## go, pipe, reduce에서 비동기 제어
4 |
5 |
--------------------------------------------------------------------------------
/09. 비동기 동시성 프로그래밍/1/html/1-3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## promise.then의 중요한 규칙
4 |
5 |
--------------------------------------------------------------------------------
/09. 비동기 동시성 프로그래밍/1/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | const isIterable = a => a && a[Symbol.iterator];
7 |
8 | const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
9 |
10 | const reduce = curry((f, acc, iter) => {
11 | if (!iter) {
12 | iter = acc[Symbol.iterator]();
13 | acc = iter.next().value;
14 | } else {
15 | iter = iter[Symbol.iterator]();
16 | }
17 | return go1(acc, function recur(acc) {
18 | let cur;
19 | while (!(cur = iter.next()).done) {
20 | const a = cur.value;
21 | acc = f(acc, a);
22 | if (acc instanceof Promise) return acc.then(recur);
23 | }
24 | return acc;
25 | });
26 | });
27 |
28 | const go = (...args) => reduce((a, f) => f(a), args);
29 |
30 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
31 |
32 | const take = curry((l, iter) => {
33 | let res = [];
34 | iter = iter[Symbol.iterator]();
35 | let cur;
36 | while (!(cur = iter.next()).done) {
37 | const a = cur.value;
38 | res.push(a);
39 | if (res.length == l) return res;
40 | }
41 | return res;
42 | });
43 |
44 | const takeAll = take(Infinity);
45 |
46 | const L = {};
47 |
48 | L.range = function* (l) {
49 | let i = -1;
50 | while (++i < l) yield i;
51 | };
52 |
53 | L.map = curry(function* (f, iter) {
54 | for (const a of iter) {
55 | yield f(a);
56 | }
57 | });
58 |
59 | L.filter = curry(function* (f, iter) {
60 | for (const a of iter) {
61 | if (f(a)) yield a;
62 | }
63 | });
64 |
65 | L.entries = function* (obj) {
66 | for (const k in obj) yield [k, obj[k]];
67 | };
68 |
69 | L.flatten = function* (iter) {
70 | for (const a of iter) {
71 | if (isIterable(a)) yield* a;
72 | else yield a;
73 | }
74 | };
75 |
76 | L.deepFlat = function* f(iter) {
77 | for (const a of iter) {
78 | if (isIterable(a)) yield* f(a);
79 | else yield a;
80 | }
81 | };
82 |
83 | L.flatMap = curry(pipe(L.map, L.flatten));
84 |
85 | const map = curry(pipe(L.map, takeAll));
86 |
87 | const filter = curry(pipe(L.filter, takeAll));
88 |
89 | const find = curry((f, iter) => go(
90 | iter,
91 | L.filter(f),
92 | take(1),
93 | ([a]) => a));
94 |
95 | const flatten = pipe(L.flatten, takeAll);
96 |
97 | const flatMap = curry(pipe(L.map, flatten));
98 |
99 | var add = (a, b) => a + b;
100 |
101 | const range = l => {
102 | let i = -1;
103 | let res = [];
104 | while (++i < l) {
105 | res.push(i);
106 | }
107 | return res;
108 | };
109 |
--------------------------------------------------------------------------------
/10. 비동기 동시성 프로그래밍 2/1/html/1-1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## 지연 평가 + Promise - L.map, map, take
4 |
5 |
39 |
40 | ## Kleisli Composition - L.filter, filter, nop, take
41 |
42 |
59 |
60 | ## reduce에서 nop 지원
61 |
62 |
69 |
70 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/10. 비동기 동시성 프로그래밍 2/1/html/1-2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## 지연된 함수열을 병렬적으로 평가하기 - C.reduce, C.take
4 |
5 |
40 |
41 | ## 즉시 병렬적으로 평가하기 - C.map, C.filter
42 |
43 |
47 |
48 | ## 즉시, 지연, Promise, 병렬적 조합하기
49 |
50 |
--------------------------------------------------------------------------------
/10. 비동기 동시성 프로그래밍 2/1/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | const isIterable = a => a && a[Symbol.iterator];
7 |
8 | const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
9 |
10 | const reduceF = (acc, a, f) =>
11 | a instanceof Promise ?
12 | a.then(a => f(acc, a), e => e == nop ? acc : Promise.reject(e)) :
13 | f(acc, a);
14 |
15 | const head = iter => go1(take(1, iter), ([h]) => h);
16 |
17 | const reduce = curry((f, acc, iter) => {
18 | if (!iter) return reduce(f, head(iter = acc[Symbol.iterator]()), iter);
19 |
20 | iter = iter[Symbol.iterator]();
21 | return go1(acc, function recur(acc) {
22 | let cur;
23 | while (!(cur = iter.next()).done) {
24 | acc = reduceF(acc, cur.value, f);
25 | if (acc instanceof Promise) return acc.then(recur);
26 | }
27 | return acc;
28 | });
29 | });
30 |
31 | const go = (...args) => reduce((a, f) => f(a), args);
32 |
33 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
34 |
35 | const take = curry((l, iter) => {
36 | let res = [];
37 | iter = iter[Symbol.iterator]();
38 | return function recur() {
39 | let cur;
40 | while (!(cur = iter.next()).done) {
41 | const a = cur.value;
42 | if (a instanceof Promise) {
43 | return a
44 | .then(a => (res.push(a), res).length == l ? res : recur())
45 | .catch(e => e == nop ? recur() : Promise.reject(e));
46 | }
47 | res.push(a);
48 | if (res.length == l) return res;
49 | }
50 | return res;
51 | }();
52 | });
53 |
54 | const takeAll = take(Infinity);
55 |
56 | const L = {};
57 |
58 | L.range = function* (l) {
59 | let i = -1;
60 | while (++i < l) yield i;
61 | };
62 |
63 | L.map = curry(function* (f, iter) {
64 | for (const a of iter) {
65 | yield go1(a, f);
66 | }
67 | });
68 |
69 | const nop = Symbol('nop');
70 |
71 | L.filter = curry(function* (f, iter) {
72 | for (const a of iter) {
73 | const b = go1(a, f);
74 | if (b instanceof Promise) yield b.then(b => b ? a : Promise.reject(nop));
75 | else if (b) yield a;
76 | }
77 | });
78 |
79 | L.entries = function* (obj) {
80 | for (const k in obj) yield [k, obj[k]];
81 | };
82 |
83 | L.flatten = function* (iter) {
84 | for (const a of iter) {
85 | if (isIterable(a)) yield* a;
86 | else yield a;
87 | }
88 | };
89 |
90 | L.deepFlat = function* f(iter) {
91 | for (const a of iter) {
92 | if (isIterable(a)) yield* f(a);
93 | else yield a;
94 | }
95 | };
96 |
97 | L.flatMap = curry(pipe(L.map, L.flatten));
98 |
99 | const map = curry(pipe(L.map, takeAll));
100 |
101 | const filter = curry(pipe(L.filter, takeAll));
102 |
103 | const find = curry((f, iter) => go(
104 | iter,
105 | L.filter(f),
106 | take(1),
107 | ([a]) => a));
108 |
109 | const flatten = pipe(L.flatten, takeAll);
110 |
111 | const flatMap = curry(pipe(L.map, flatten));
112 |
113 | var add = (a, b) => a + b;
114 |
115 | const range = l => {
116 | let i = -1;
117 | let res = [];
118 | while (++i < l) {
119 | res.push(i);
120 | }
121 | return res;
122 | };
123 |
--------------------------------------------------------------------------------
/11. 비동기 동시성 프로그래밍 3/1/html/1-1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## async/await
4 |
5 |
36 |
37 | ## QnA. Array.prototype.map이 있는데 왜 FxJS의 map 함수가 필요한지?
38 |
39 |
72 |
73 | ## QnA. 이제 비동기는 async/await로 제어할 수 있는데 왜 파이프라인이 필요한지?
74 |
75 |
106 |
107 | ## QnA. async/await와 파이프라인을 같이 사용하기도 하나요?
108 |
109 |
130 |
131 | ## QnA. 동기 상황에서 에러 핸들링은 어떻게 해야하는지?
132 |
133 |
147 |
148 | ## QnA. 비동기 상황에서 에러 핸들링은 어떻게 해야하는지?
149 |
150 |
168 |
169 | ## QnA. 동기/비동기 에러 핸들링에서의 파이프라인의 이점은?
170 |
171 |
190 |
--------------------------------------------------------------------------------
/11. 비동기 동시성 프로그래밍 3/1/lib/fx.js:
--------------------------------------------------------------------------------
1 | const log = console.log;
2 |
3 | const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | const isIterable = a => a && a[Symbol.iterator];
7 |
8 | const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
9 |
10 | const reduceF = (acc, a, f) =>
11 | a instanceof Promise ?
12 | a.then(a => f(acc, a), e => e == nop ? acc : Promise.reject(e)) :
13 | f(acc, a);
14 |
15 | const head = iter => go1(take(1, iter), ([h]) => h);
16 |
17 | const reduce = curry((f, acc, iter) => {
18 | if (!iter) return reduce(f, head(iter = acc[Symbol.iterator]()), iter);
19 |
20 | iter = iter[Symbol.iterator]();
21 | return go1(acc, function recur(acc) {
22 | let cur;
23 | while (!(cur = iter.next()).done) {
24 | acc = reduceF(acc, cur.value, f);
25 | if (acc instanceof Promise) return acc.then(recur);
26 | }
27 | return acc;
28 | });
29 | });
30 |
31 | const go = (...args) => reduce((a, f) => f(a), args);
32 |
33 | const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
34 |
35 | const take = curry((l, iter) => {
36 | let res = [];
37 | iter = iter[Symbol.iterator]();
38 | return function recur() {
39 | let cur;
40 | while (!(cur = iter.next()).done) {
41 | const a = cur.value;
42 | if (a instanceof Promise) {
43 | return a
44 | .then(a => (res.push(a), res).length == l ? res : recur())
45 | .catch(e => e == nop ? recur() : Promise.reject(e));
46 | }
47 | res.push(a);
48 | if (res.length == l) return res;
49 | }
50 | return res;
51 | }();
52 | });
53 |
54 | const takeAll = take(Infinity);
55 |
56 | const L = {};
57 |
58 | L.range = function* (l) {
59 | let i = -1;
60 | while (++i < l) yield i;
61 | };
62 |
63 | L.map = curry(function* (f, iter) {
64 | for (const a of iter) {
65 | yield go1(a, f);
66 | }
67 | });
68 |
69 | const nop = Symbol('nop');
70 |
71 | L.filter = curry(function* (f, iter) {
72 | for (const a of iter) {
73 | const b = go1(a, f);
74 | if (b instanceof Promise) yield b.then(b => b ? a : Promise.reject(nop));
75 | else if (b) yield a;
76 | }
77 | });
78 |
79 | L.entries = function* (obj) {
80 | for (const k in obj) yield [k, obj[k]];
81 | };
82 |
83 | L.flatten = function* (iter) {
84 | for (const a of iter) {
85 | if (isIterable(a)) yield* a;
86 | else yield a;
87 | }
88 | };
89 |
90 | L.deepFlat = function* f(iter) {
91 | for (const a of iter) {
92 | if (isIterable(a)) yield* f(a);
93 | else yield a;
94 | }
95 | };
96 |
97 | L.flatMap = curry(pipe(L.map, L.flatten));
98 |
99 | const map = curry(pipe(L.map, takeAll));
100 |
101 | const filter = curry(pipe(L.filter, takeAll));
102 |
103 | const find = curry((f, iter) => go(
104 | iter,
105 | L.filter(f),
106 | take(1),
107 | ([a]) => a));
108 |
109 | const flatten = pipe(L.flatten, takeAll);
110 |
111 | const flatMap = curry(pipe(L.map, flatten));
112 |
113 | var add = (a, b) => a + b;
114 |
115 | const range = l => {
116 | let i = -1;
117 | let res = [];
118 | while (++i < l) {
119 | res.push(i);
120 | }
121 | return res;
122 | };
123 |
124 | const C = {};
125 |
126 | function noop() {
127 | }
128 |
129 | const catchNoop = ([...arr]) =>
130 | (arr.forEach(a => a instanceof Promise ? a.catch(noop) : a), arr);
131 |
132 | C.reduce = curry((f, acc, iter) => iter ?
133 | reduce(f, acc, catchNoop(iter)) :
134 | reduce(f, catchNoop(acc)));
135 |
136 | C.take = curry((l, iter) => take(l, catchNoop(iter)));
137 |
138 | C.takeAll = C.take(Infinity);
139 |
140 | C.map = curry(pipe(L.map, C.takeAll));
141 |
142 | C.filter = curry(pipe(L.filter, C.takeAll));
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 함수형 프로그래밍과 ES6+ 예제 코드
2 |
3 | ## 강의 목록
4 | 1. 함수형 자바스크립트 기본기
5 | 1. 평가와 일급
6 | 2. 일급 함수
7 | 3. 고차 함수
8 | 4. 부수효과와 순수 함수
9 | 2. ES6에서의 순회와 이터러블/이터레이터 프로토콜
10 | 1. 기존과 달라진 ES6에서의 리스트 순회
11 | 2. Array, Set, Map을 통해 알아보는 이터러블/이터레이터 프로토콜
12 | 3. 사용자 정의 이터러블
13 | 4. 전개 연산자
14 | 3. 제너레이터와 이터레이터
15 | 1. 제너레이터와 이터레이터
16 | 2. odds
17 | 3. for...of, 전개 연산자, 구조 분해, 나머지 연산자
18 | 4. map, filter, reduce
19 | 1. map
20 | 2. 이터러블 프로토콜을 따른 map의 다형성 1
21 | 3. 이터러블 프로토콜을 따른 map의 다형성 2
22 | 4. filter
23 | 5. reduce
24 | 6. reduce 2
25 | 7. map+filter+reduce 중첩 사용과 함수형 사고
26 | 5. 코드를 값으로 다루어 표현력 높이기
27 | 1. go
28 | 2. pipe
29 | 3. go를 사용하여 읽기 좋은 코드로 만들기
30 | 4. go+curry를 사용하여 더 읽기 좋은 코드로 만들기
31 | 5. 함수 조합으로 함수 만들기
32 | 6. 장바구니 예제
33 | 1. 총 수량, 총 가격
34 | 2. HTML로 출력하기
35 | 7. 지연성 1
36 | 1. range와 느긋한 L.range
37 | 2. range와 느긋한 L.range 테스트
38 | 3. take
39 | 4. 제너레이터/이터레이터 프로토콜로 구현하는 지연 평가
40 | 5. L.map
41 | 6. L. filter
42 | 7. range, map, filter, take, reduce 중첩 사용
43 | 8. L.range, L.map, L.filter, take 의 평가 순서
44 | 9. 엄격한 계산과 느긋한 계산의 효율성 비교
45 | 10. map, filter 계열 함수들이 가지는 결합 법칙
46 | 11. ES6의 기본 규악을 통해 구현하는 지연 평가의 장점
47 | 8. 지연성 2
48 | 1. 결과를 만드는 함수 reduce, take
49 | 2. queryStr 함수 만들기
50 | 3. Array.prototype.join 보다 다형성이 높은 join 함수
51 | 4. take, find
52 | 5. L.map, L.filter로 map과 filter 만들기
53 | 6. L.flatten, flatten
54 | 7. L.flatMap, flatMap
55 | 8. 2차원 배열 다루기
56 | 9. 지연성 / 이터러블 중심 프로그래밍 실무적인 코드
57 | 9. 비동기/동시성 프로그래밍 1
58 | 1. callback과 Promise
59 | 2. 비동기를 값으로 만드는 Promise
60 | 3. 값으로서의 Promise 활용
61 | 4. 합성 관점에서의 Promise와 모나드
62 | 5. Kleisli Composition 관점에서의 Promise
63 | 6. go, pipe, reduce에서 비동기 제어
64 | 7. promise.then의 중요한 규칙
65 | 10. 비동기/동시성 프로그래밍 2
66 | 1. 지연 평가 + Promise - L.map, map, take
67 | 2. Kleisli Composition - L.filter, filter, nop, take
68 | 3. reduce에서 nop 지원
69 | 4. 지연 평가 + Promise의 효율성
70 | 5. 지연된 함수열을 병렬적으로 평가하기 - C.reduce, C.take 1
71 | 6. 지연된 함수열을 병렬적으로 평가하기 - C.reduce, C.take 2
72 | 7. 즉시 병렬적으로 평가하기 - C.map, C.filter
73 | 8. 즉시, 지연, Promise, 병렬적 조합하기
74 | 9. 코드 간단히 정리
75 | 10. Node.js에서 SQL 병렬 평가로 얻은 효율
76 | 11. 비동기/동시성 프로그래밍 3
77 | 1. async/await
78 | 2. Q&A) Array.prototype.map이 있는데 왜 FxJS의 map 함수가 필요한지?
79 | 3. Q&A) 이제 비동기는 async/await로 제어할 수 있는데 왜 파이프라인이 필요한지?
80 | 4. Q&A) async/await와 파이프라인을 같이 사용하기도 하는지?
81 | 5. Q&A) 동기 상황에서 에러 핸들링은 어떻게 해야하는지?
82 | 6. Q&A) 비동기 상황에서 에러 핸들링은 어떻게 해야하는지?
83 | 7. Q&A) 동기/비동기 에러 핸들링에서의 파이프라인의 이점은?
84 |
--------------------------------------------------------------------------------
/fx.js:
--------------------------------------------------------------------------------
1 | export const log = console.log;
2 |
3 | export const curry = f =>
4 | (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
5 |
6 | export const isIterable = a => a && a[Symbol.iterator];
7 |
8 | export const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
9 |
10 | const reduceF = (acc, a, f) =>
11 | a instanceof Promise ?
12 | a.then(a => f(acc, a), e => e == nop ? acc : Promise.reject(e)) :
13 | f(acc, a);
14 |
15 | export const head = iter => go1(take(1, iter), ([h]) => h);
16 |
17 | export const reduce = curry((f, acc, iter) => {
18 | if (!iter) return reduce(f, head(iter = acc[Symbol.iterator]()), iter);
19 |
20 | iter = iter[Symbol.iterator]();
21 | return go1(acc, function recur(acc) {
22 | let cur;
23 | while (!(cur = iter.next()).done) {
24 | acc = reduceF(acc, cur.value, f);
25 | if (acc instanceof Promise) return acc.then(recur);
26 | }
27 | return acc;
28 | });
29 | });
30 |
31 | export const go = (...args) => reduce((a, f) => f(a), args);
32 |
33 | export const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
34 |
35 | export const take = curry((l, iter) => {
36 | let res = [];
37 | iter = iter[Symbol.iterator]();
38 | return function recur() {
39 | let cur;
40 | while (!(cur = iter.next()).done) {
41 | const a = cur.value;
42 | if (a instanceof Promise) {
43 | return a
44 | .then(a => (res.push(a), res).length == l ? res : recur())
45 | .catch(e => e == nop ? recur() : Promise.reject(e));
46 | }
47 | res.push(a);
48 | if (res.length == l) return res;
49 | }
50 | return res;
51 | }();
52 | });
53 |
54 | export const takeAll = take(Infinity);
55 |
56 | export const L = {};
57 |
58 | L.range = function* (l) {
59 | let i = -1;
60 | while (++i < l) yield i;
61 | };
62 |
63 | L.map = curry(function* (f, iter) {
64 | for (const a of iter) {
65 | yield go1(a, f);
66 | }
67 | });
68 |
69 | export const nop = Symbol('nop');
70 |
71 | L.filter = curry(function* (f, iter) {
72 | for (const a of iter) {
73 | const b = go1(a, f);
74 | if (b instanceof Promise) yield b.then(b => b ? a : Promise.reject(nop));
75 | else if (b) yield a;
76 | }
77 | });
78 |
79 | L.entries = function* (obj) {
80 | for (const k in obj) yield [k, obj[k]];
81 | };
82 |
83 | L.flatten = function* (iter) {
84 | for (const a of iter) {
85 | if (isIterable(a) && typeof a != 'string') yield* a;
86 | else yield a;
87 | }
88 | };
89 |
90 | L.deepFlat = function* f(iter) {
91 | for (const a of iter) {
92 | if (isIterable(a) && typeof a != 'string') yield* f(a);
93 | else yield a;
94 | }
95 | };
96 |
97 | L.flatMap = curry(pipe(L.map, L.flatten));
98 |
99 | export const map = curry(pipe(L.map, takeAll));
100 |
101 | export const filter = curry(pipe(L.filter, takeAll));
102 |
103 | export const find = curry((f, iter) => go(
104 | iter,
105 | L.filter(f),
106 | take(1),
107 | ([a]) => a));
108 |
109 | export const flatten = pipe(L.flatten, takeAll);
110 |
111 | export const flatMap = curry(pipe(L.map, flatten));
112 |
113 | export const add = (a, b) => a + b;
114 |
115 | export const range = l => {
116 | let i = -1;
117 | let res = [];
118 | while (++i < l) {
119 | res.push(i);
120 | }
121 | return res;
122 | };
123 |
124 | export const C = {};
125 |
126 | export function noop() {
127 | }
128 |
129 | const catchNoop = ([...arr]) =>
130 | (arr.forEach(a => a instanceof Promise ? a.catch(noop) : a), arr);
131 |
132 | C.reduce = curry((f, acc, iter) => iter ?
133 | reduce(f, acc, catchNoop(iter)) :
134 | reduce(f, catchNoop(acc)));
135 |
136 | C.take = curry((l, iter) => take(l, catchNoop(iter)));
137 |
138 | C.takeAll = C.take(Infinity);
139 |
140 | C.map = curry(pipe(L.map, C.takeAll));
141 |
142 | C.filter = curry(pipe(L.filter, C.takeAll));
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('esm')(module)('./fx.js');
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | const { expect } = require('chai');
2 |
3 | Object.assign(global, require('../index.js'));
4 |
5 | describe('take', function () {
6 | it('take(1, [1, 2, 3])', () => {
7 | expect(take(1, [1, 2, 3])).to.eql([1]);
8 | });
9 | it('take(2, [1, 2, 3])', () => {
10 | expect(take(2, [1, 2, 3])).to.eql([1, 2]);
11 | });
12 |
13 | it('take(2, L.map(a => a + 1, [1, 2, 3]))', () => {
14 | expect(take(2, L.map(a => a + 1, [1, 2, 3]))).to.eql([2, 3]);
15 | });
16 |
17 | it('take(2, L.filter(a => a % 2, [1, 2, 3, 4, 5]))', () => {
18 | expect(take(2, L.filter(a => a % 2, [1, 2, 3, 4, 5]))).to.eql([1, 3]);
19 | });
20 |
21 | it('take(2, L.map(a => Promise.resolve(a + 1), [1, 2, 3]))', () => {
22 | go1(
23 | take(2, L.map(a => Promise.resolve(a + 1), [1, 2, 3])),
24 | _ => expect(_).to.eql([2, 3])
25 | );
26 | });
27 |
28 | it('take(2, L.filter(a => Promise.resolve(a % 2), [1, 2, 3, 4, 5]))', () => {
29 | go1(
30 | take(2, L.filter(a => Promise.resolve(a % 2), [1, 2, 3])),
31 | _ => expect(_).to.eql([1, 3])
32 | );
33 | });
34 | });
35 |
36 | describe('reduce', function () {
37 | const add = (a, b) => a + b;
38 | it('reduce(add, [1, 2, 3])', () => {
39 | expect(reduce(add, [1, 2, 3])).to.eql(6);
40 | });
41 | it('reduce(add, 10, [1, 2, 3])', () => {
42 | expect(reduce(add, 10, [1, 2, 3])).to.eql(16);
43 | });
44 | it('reduce(add, [Promise.resolve(1), 2, 3])', () => {
45 | go1(reduce(add, [Promise.resolve(1), 2, 3]), _ => expect(_).to.eql(6));
46 | });
47 | it('reduce(add, L.filter(a => a % 2, [1, 2, 3, 4]))', () => {
48 | expect(reduce(add, L.filter(a => a % 2, [1, 2, 3, 4]))).to.eql(4);
49 | });
50 | it('reduce(add, L.filter(a => Promise.resolve(a % 2), [1, 2, 3, 4]))', () => {
51 | go1(reduce(add, L.filter(a => Promise.resolve(a % 2), [1, 2, 3, 4])), _ => expect(_).to.eql(4));
52 | });
53 | it('reduce(add, L.filter(a => Promise.resolve(a % 2), L.map(a => a + 10, [1, 2, 3, 4])))', () => {
54 | go1(reduce(add, L.filter(a => Promise.resolve(a % 2), L.map(a => a + 10, [1, 2, 3, 4]))), _ => expect(_).to.eql(24));
55 | });
56 | it('reduce(add, L.filter(a => Promise.resolve(a % 2), L.map(a => Promise.resolve(a + 10), [1, 2, 3, 4])))', () => {
57 | go1(reduce(add, L.filter(a => Promise.resolve(a % 2), L.map(a => Promise.resolve(a + 10), [1, 2, 3, 4]))), _ => expect(_).to.eql(24));
58 | });
59 | });
60 |
61 | describe('C.take', function () {
62 | function delay(val, time = 1000) {
63 | return new Promise(resolve => setTimeout(_ => resolve(val), time));
64 | }
65 |
66 | it('C.take(2, L.map(a => delay(a, a * 100), [1, 2, 3]))', () => {
67 | go1(
68 | C.take(2, L.map(a => delay(a, 100), [1, 2, 3])),
69 | _ => expect(_).to.eql([1, 2])
70 | );
71 | });
72 |
73 | it('C.take(2, L.filter(a => delay(a % 2, a * 100), [1, 2, 3]))', () => {
74 | go1(
75 | C.take(2, L.filter(a => delay(a % 2, 100), [1, 2, 3])),
76 | _ => expect(_).to.eql([1, 3])
77 | );
78 | });
79 | });
80 |
81 | describe('map', function () {
82 | return;
83 | function delay(val, time = 1000) {
84 | return new Promise(resolve => setTimeout(_ => resolve(val), time));
85 | }
86 |
87 | it('map(a => delay(a+10, 1000), [1, 2, 3])', async function() {
88 | expect(await map(a => a + 10, [Promise.resolve(1), 2, 3])).to.eql([11, 12, 13]);
89 | });
90 |
91 | it('map(a => delay(a+10, 1000), [1, 2, 3])', async function() {
92 | this.timeout(5000);
93 | expect(await map(a => delay(a+10, 1000), [1, 2, 3])).to.eql([11, 12, 13]);
94 | });
95 |
96 | it('C.map(a => delay(a+10, 1000), [1, 2, 3])', async function() {
97 | this.timeout(1500);
98 | expect(await C.map(a => delay(a+10, 1000), [1, 2, 3])).to.eql([11, 12, 13]);
99 | });
100 | });
101 |
102 | describe('find', function () {
103 | it('find(a => a > 1, [1, 2, 3])', function() {
104 | expect(find(a => a > 1, [1, 2, 3])).to.eql(2);
105 | });
106 | });
107 |
108 | describe('go', function () {
109 | it(`
110 | go(
111 | 0,
112 | a => a + 1,
113 | a => a + 10,
114 | a => a + 100)`, function () {
115 | expect(
116 | go(
117 | 0,
118 | a => a + 1,
119 | a => a + 10,
120 | a => a + 100)).to.eql(111)
121 | });
122 |
123 | it(`
124 | go(
125 | 0,
126 | a => { throw { hi: 'ho' } },
127 | a => a + 10,
128 | a => a + 100)`, function () {
129 | try {
130 | go(
131 | 0,
132 | a => { throw { hi: 'ho' } },
133 | a => a + 10,
134 | a => a + 100)
135 | } catch (a) {
136 | expect(a).to.eql({ hi: 'ho' })
137 | }
138 | });
139 |
140 | it(`
141 | go(
142 | 0,
143 | a => Promise.resolve(a + 1),
144 | a => a + 10,
145 | a => a + 100)`, async function () {
146 | expect(
147 | await go(
148 | 0,
149 | a => Promise.resolve(a + 1),
150 | a => a + 10,
151 | a => a + 100)).to.eql(111)
152 | });
153 |
154 | it(`
155 | go(
156 | 0,
157 | a => Promise.resolve(a + 1),
158 | a => Promise.reject({ hi: 'ho' }),
159 | a => a + 100)`, async function () {
160 | try {
161 | await go(
162 | 0,
163 | a => Promise.resolve(a + 1),
164 | a => Promise.reject({ hi: 'ho' }),
165 | a => a + 100)
166 | } catch (a) {
167 | expect(a).to.eql({ hi: 'ho' })
168 | }
169 | });
170 |
171 | it(`
172 | go(
173 | 0,
174 | a => Promise.resolve(a + 1),
175 | a => { throw { hi: 'ho' } },
176 | a => a + 100)`, async function () {
177 | try {
178 | await go(
179 | 0,
180 | a => Promise.resolve(a + 1),
181 | a => { throw { hi: 'ho' } },
182 | a => a + 100)
183 | } catch (a) {
184 | expect(a).to.eql({ hi: 'ho' })
185 | }
186 | });
187 | });
--------------------------------------------------------------------------------