├── .babelrc
├── .gitignore
├── example
├── index.html
├── index.js
└── index.out.js
├── package.json
└── src
├── combinators.js
├── frp
├── basic-idea.js
├── duality.js
└── reactive.js
├── index.js
├── test.js
└── utils.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"]
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | build
4 | node_modules
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Simple clicker :D
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | import {fromEvent, map, scan, mergeArray, interval, of} from '../build/frp/reactive';
2 | import {compose} from '../build';
3 |
4 | const minus$ = map(x => -1)(fromEvent('click')(document.getElementById('minus')));
5 | const plus$ = map(x => 1)(fromEvent('click')(document.getElementById('plus')));
6 |
7 | const dispose = compose(
8 | scan((acc, next) => Math.min(10, Math.max(-10, acc + next)))(0),
9 | mergeArray
10 | )([plus$, minus$, of(0), map(x => 1)(interval(2000))])({next: v => {
11 | document.getElementById('display').innerHTML = v;
12 | }});
--------------------------------------------------------------------------------
/example/index.out.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o a
8 | var I = function I(x) {
9 | return x;
10 | };
11 |
12 | // K :: a -> b -> a
13 | var K = function K(x) {
14 | return function (y) {
15 | return x;
16 | };
17 | };
18 |
19 | // C :: (a -> b -> c) -> b -> a -> c
20 | var C = function C(x) {
21 | return function (y) {
22 | return function (z) {
23 | return x(z)(y);
24 | };
25 | };
26 | };
27 |
28 | // S :: (a -> b -> c) -> (a -> b) -> a -> c
29 | var S = function S(x) {
30 | return function (y) {
31 | return function () {
32 | return x.apply(undefined, arguments)(y.apply(undefined, arguments));
33 | };
34 | };
35 | };
36 |
37 | // B :: (b -> c) -> (a -> b) -> a -> c
38 | var B = S(K(S))(K);
39 |
40 | // M :: a -> a -> a
41 | var M = function M(x) {
42 | return x(x);
43 | };
44 |
45 | var Y = function Y(f) {
46 | return M(function (x) {
47 | return function (y) {
48 | return f(x(x))(y);
49 | };
50 | });
51 | };
52 |
53 | // A :: (a -> a) -> a -> a
54 | var A = C(B(B)(I))(I);
55 |
56 | // T :: a -> (a -> a) -> a
57 | var T = C(A);
58 |
59 | exports.I = I;
60 | exports.K = K;
61 | exports.C = C;
62 | exports.S = S;
63 | exports.B = B;
64 | exports.M = M;
65 | exports.Y = Y;
66 | exports.A = A;
67 | exports.T = T;
68 |
69 | },{}],2:[function(require,module,exports){
70 | 'use strict';
71 |
72 | Object.defineProperty(exports, "__esModule", {
73 | value: true
74 | });
75 |
76 | function _toConsumableArray(arr) {
77 | if (Array.isArray(arr)) {
78 | for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
79 | arr2[i] = arr[i];
80 | }return arr2;
81 | } else {
82 | return Array.from(arr);
83 | }
84 | }
85 |
86 | var disposable = function disposable() {
87 | for (var _len = arguments.length, disposables = Array(_len), _key = 0; _key < _len; _key++) {
88 | disposables[_key] = arguments[_key];
89 | }
90 |
91 | return function () {
92 | return disposables.forEach(function (dispose) {
93 | return dispose();
94 | });
95 | };
96 | };
97 |
98 | var basicObserver = {
99 | next: function next(v) {},
100 | error: function error(e) {},
101 | completed: function completed() {},
102 | disposed: false
103 | };
104 |
105 | // of :: a -> Observable a
106 | var of = function of(value) {
107 | return function () {
108 | var _ref = arguments.length <= 0 || arguments[0] === undefined ? basicObserver : arguments[0];
109 |
110 | var next = _ref.next;
111 | var error = _ref.error;
112 | var completed = _ref.completed;
113 | var disposed = _ref.disposed;
114 | return next(value), function () {
115 | return console.log('of disposed!');
116 | };
117 | };
118 | };
119 |
120 | // fromEvent :: String -> DomElement -> Observable Event
121 | var fromEvent = function fromEvent(name) {
122 | return function (element) {
123 | return function () {
124 | var _ref2 = arguments.length <= 0 || arguments[0] === undefined ? basicObserver : arguments[0];
125 |
126 | var next = _ref2.next;
127 | var error = _ref2.error;
128 | var completed = _ref2.completed;
129 | var disposed = _ref2.disposed;
130 |
131 | element.addEventListener(name, function (e) {
132 | return disposed !== true && next(e);
133 | });
134 |
135 | return function () {
136 | disposed = true;
137 | console.log('fromEvent disposed!');
138 | element.removeEventListener(name, function (e) {
139 | return next(e);
140 | });
141 | };
142 | };
143 | };
144 | };
145 |
146 | // interval :: Number -> Observable Number
147 | var interval = function interval(time) {
148 | return function () {
149 | var _ref3 = arguments.length <= 0 || arguments[0] === undefined ? basicObserver : arguments[0];
150 |
151 | var next = _ref3.next;
152 | var error = _ref3.error;
153 | var completed = _ref3.completed;
154 | var disposed = _ref3.disposed;
155 |
156 | var i = 0;
157 | var id = setInterval(function () {
158 | disposed !== true && next(++i);
159 | }, time);
160 |
161 | return function () {
162 | disposed = true;
163 | console.log('interval disposed!');
164 | clearInterval(id);
165 | };
166 | };
167 | };
168 |
169 | // map :: (a -> b) -> Observable a -> Observable b
170 | var map = function map(fn) {
171 | return function (source) {
172 | return function () {
173 | var _ref4 = arguments.length <= 0 || arguments[0] === undefined ? basicObserver : arguments[0];
174 |
175 | var _next = _ref4.next;
176 | var error = _ref4.error;
177 | var completed = _ref4.completed;
178 | var disposed = _ref4.disposed;
179 | return source({ next: function next(v) {
180 | return _next(fn(v));
181 | } });
182 | };
183 | };
184 | };
185 |
186 | // filter :: (a -> bool) -> Observable a -> Observable a
187 | var filter = function filter(predicate) {
188 | return function (source) {
189 | return function (o) {
190 | return source({ next: function next(v) {
191 | return predicate(v) && o.next(v);
192 | } });
193 | };
194 | };
195 | };
196 |
197 | // scan :: (b -> a -> b) -> b -> Observable a -> Observable b
198 | var scan = function scan(acc) {
199 | return function (seed) {
200 | var accumulation = arguments.length <= 1 || arguments[1] === undefined ? seed : arguments[1];
201 | return function (source) {
202 | return function () {
203 | var _ref5 = arguments.length <= 0 || arguments[0] === undefined ? basicObserver : arguments[0];
204 |
205 | var _next2 = _ref5.next;
206 | var error = _ref5.error;
207 | var completed = _ref5.completed;
208 | var disposed = _ref5.disposed;
209 |
210 | return source({ next: function next(v) {
211 | return _next2((accumulation = acc(accumulation, v), accumulation));
212 | } });
213 | };
214 | };
215 | };
216 | };
217 |
218 | // mergeArray :: [Observable a] -> Observable a
219 | var mergeArray = function mergeArray(streams) {
220 | return function () {
221 | var _ref6 = arguments.length <= 0 || arguments[0] === undefined ? basicObserver : arguments[0];
222 |
223 | var _next3 = _ref6.next;
224 | var error = _ref6.error;
225 | var completed = _ref6.completed;
226 | var disposed = _ref6.disposed;
227 |
228 | return disposable.apply(undefined, _toConsumableArray(streams.map(function (stream) {
229 | return stream({ next: function next(v) {
230 | return _next3(v);
231 | } });
232 | })));
233 | };
234 | };
235 |
236 | exports.basicObserver = basicObserver;
237 | exports.disposable = disposable;
238 | exports.of = of;
239 | exports.interval = interval;
240 | exports.fromEvent = fromEvent;
241 | exports.map = map;
242 | exports.filter = filter;
243 | exports.scan = scan;
244 | exports.mergeArray = mergeArray;
245 |
246 | },{}],3:[function(require,module,exports){
247 | 'use strict';
248 |
249 | Object.defineProperty(exports, "__esModule", {
250 | value: true
251 | });
252 | exports.concat = exports.foldMap = exports.fold = exports.compose = exports.juxt = exports.converge = exports.lift = exports.ap = exports.filter = exports.map = exports.flatMap = exports.of = exports.reduce = undefined;
253 |
254 | var _combinators = require('./combinators');
255 |
256 | var _utils = require('./utils');
257 |
258 | var reduce = function reduce(f) {
259 | return (0, _combinators.Y)(function (g) {
260 | return function (y) {
261 | return function (xs) {
262 | return (0, _utils.length)(xs) < 1 ? y : g(f(y)((0, _utils.head)(xs)))((0, _utils.tail)(xs));
263 | };
264 | };
265 | });
266 | };
267 |
268 | var of = function of() {
269 | for (var _len = arguments.length, value = Array(_len), _key = 0; _key < _len; _key++) {
270 | value[_key] = arguments[_key];
271 | }
272 |
273 | return Array.of.apply(Array, value) || [].concat(value);
274 | };
275 |
276 | var empty = function empty() {
277 | return [];
278 | };
279 |
280 | var concat = function concat(x) {
281 | return function (y) {
282 | return x.concat(y);
283 | };
284 | };
285 |
286 | var flatMap = function flatMap(fn) {
287 | return reduce(function (acc) {
288 | return function (next) {
289 | return concat(acc)(fn(next));
290 | };
291 | })(empty());
292 | };
293 |
294 | var fold = reduce(concat)(empty());
295 |
296 | var foldMap = function foldMap(fn) {
297 | return reduce(function (acc) {
298 | return function (next) {
299 | return concat(acc)(fn(next));
300 | };
301 | })(empty());
302 | };
303 |
304 | var map = function map(fn) {
305 | return flatMap(function (value) {
306 | return of(fn(value));
307 | });
308 | };
309 |
310 | var filter = function filter(predicate) {
311 | return flatMap(function (value) {
312 | return predicate(value) ? of(value) : empty();
313 | });
314 | };
315 |
316 | var ap = function ap(fns) {
317 | return function (source) {
318 | return flatMap(function (fn) {
319 | return map(function (value) {
320 | return fn(value);
321 | })(source);
322 | })(fns);
323 | };
324 | };
325 |
326 | var lift = function lift(fn) {
327 | return (0, _utils.unapply)(reduce(ap)(of(fn)));
328 | };
329 |
330 | var converge = function converge(resultSelector) {
331 | return function () {
332 | for (var _len2 = arguments.length, fns = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
333 | fns[_key2] = arguments[_key2];
334 | }
335 |
336 | return function () {
337 | for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
338 | args[_key3] = arguments[_key3];
339 | }
340 |
341 | return (0, _utils.apply)(resultSelector)(ap(map(function (fn) {
342 | return (0, _utils.apply)(fn);
343 | })(fns))(of(args)));
344 | };
345 | };
346 | };
347 |
348 | var juxt = (0, _utils.apply)(converge((0, _utils.unapply)(_combinators.I)));
349 |
350 | var compose = (0, _utils.unapply)(reduce(_combinators.B)(_combinators.I));
351 |
352 | exports.reduce = reduce;
353 | exports.of = of;
354 | exports.flatMap = flatMap;
355 | exports.map = map;
356 | exports.filter = filter;
357 | exports.ap = ap;
358 | exports.lift = lift;
359 | exports.converge = converge;
360 | exports.juxt = juxt;
361 | exports.compose = compose;
362 | exports.fold = fold;
363 | exports.foldMap = foldMap;
364 | exports.concat = concat;
365 |
366 | },{"./combinators":1,"./utils":4}],4:[function(require,module,exports){
367 | "use strict";
368 |
369 | Object.defineProperty(exports, "__esModule", {
370 | value: true
371 | });
372 |
373 | function _toConsumableArray(arr) {
374 | if (Array.isArray(arr)) {
375 | for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
376 | arr2[i] = arr[i];
377 | }return arr2;
378 | } else {
379 | return Array.from(arr);
380 | }
381 | }
382 |
383 | function _toArray(arr) {
384 | return Array.isArray(arr) ? arr : Array.from(arr);
385 | }
386 |
387 | var head = function head(_ref) {
388 | var _ref2 = _toArray(_ref);
389 |
390 | var _head = _ref2[0];
391 |
392 | var tail = _ref2.slice(1);
393 |
394 | return _head;
395 | };
396 | var tail = function tail(_ref3) {
397 | var _ref4 = _toArray(_ref3);
398 |
399 | var head = _ref4[0];
400 |
401 | var _tail = _ref4.slice(1);
402 |
403 | return _tail;
404 | };
405 | var length = function length(_ref5) {
406 | var _length = _ref5.length;
407 | return _length;
408 | };
409 |
410 | var _curry = function _curry(fn) {
411 | for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
412 | args[_key - 1] = arguments[_key];
413 | }
414 |
415 | return function () {
416 | for (var _len2 = arguments.length, innerArgs = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
417 | innerArgs[_key2] = arguments[_key2];
418 | }
419 |
420 | return fn.apply(undefined, args.concat(innerArgs));
421 | };
422 | };
423 |
424 | var curry = function curry(fn) {
425 | var n = arguments.length <= 1 || arguments[1] === undefined ? fn.length : arguments[1];
426 |
427 | return function () {
428 | for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
429 | args[_key3] = arguments[_key3];
430 | }
431 |
432 | if (args.length < n) {
433 | return n - args.length > 0 ? curry(_curry.apply(undefined, [fn].concat(args)), n - args.length) : _curry.apply(undefined, [fn].concat(args));
434 | } else {
435 | return fn.apply(undefined, args);
436 | }
437 | };
438 | };
439 |
440 | var apply = function apply(fn) {
441 | return function (args) {
442 | return fn.apply(undefined, _toConsumableArray(args));
443 | };
444 | };
445 | var unapply = function unapply(fn) {
446 | return function () {
447 | for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
448 | args[_key4] = arguments[_key4];
449 | }
450 |
451 | return fn(args);
452 | };
453 | };
454 |
455 | exports.head = head;
456 | exports.tail = tail;
457 | exports.length = length;
458 | exports.curry = curry;
459 | exports.apply = apply;
460 | exports.unapply = unapply;
461 |
462 | },{}],5:[function(require,module,exports){
463 | 'use strict';
464 |
465 | var _reactive = require('../build/frp/reactive');
466 |
467 | var _build = require('../build');
468 |
469 | var minus$ = (0, _reactive.map)(function (x) {
470 | return -1;
471 | })((0, _reactive.fromEvent)('click')(document.getElementById('minus')));
472 | var plus$ = (0, _reactive.map)(function (x) {
473 | return 1;
474 | })((0, _reactive.fromEvent)('click')(document.getElementById('plus')));
475 |
476 | var dispose = (0, _build.compose)((0, _reactive.scan)(function (acc, next) {
477 | return Math.min(10, Math.max(-10, acc + next));
478 | })(0), _reactive.mergeArray)([plus$, minus$, (0, _reactive.of)(0), (0, _reactive.map)(function (x) {
479 | return 1;
480 | })((0, _reactive.interval)(2000))])({ next: function next(v) {
481 | document.getElementById('display').innerHTML = v;
482 | } });
483 |
484 | },{"../build":3,"../build/frp/reactive":2}]},{},[5]);
485 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "from-combinators-to-fp",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "watch": "./node_modules/.bin/babel --watch=./src --out-dir=./build",
8 | "example": "./node_modules/.bin/watchify ./example/index.js -t babelify --outfile ./example/index.out.js"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "babel-cli": "^6.7.7",
15 | "babel-preset-es2015": "^6.6.0",
16 | "babelify": "^7.3.0",
17 | "browserify": "^13.0.0",
18 | "watchify": "^3.7.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/combinators.js:
--------------------------------------------------------------------------------
1 | // I :: a -> a
2 | const I = x => x;
3 |
4 | // K :: a -> b -> a
5 | const K = x => y => x;
6 |
7 | // C :: (a -> b -> c) -> b -> a -> c
8 | const C = x => y => z => x(z)(y);
9 |
10 | // S :: (a -> b -> c) -> (a -> b) -> a -> c
11 | const S = x => y => (...z) => x(...z)(y(...z));
12 |
13 | // B :: (b -> c) -> (a -> b) -> a -> c
14 | const B = S(K(S))(K);
15 |
16 | // M :: a -> a -> a
17 | const M = x => x(x);
18 |
19 | const Y = f => M(x => y => f(x(x))(y));
20 |
21 | // A :: (a -> a) -> a -> a
22 | const A = C((B(B))(I))(I);
23 |
24 | // T :: a -> (a -> a) -> a
25 | const T = C(A);
26 |
27 | export {I, K, C, S, B, M, Y, A, T};
--------------------------------------------------------------------------------
/src/frp/basic-idea.js:
--------------------------------------------------------------------------------
1 | const observable = value => o => {
2 | o.next(value);
3 | o.completed();
4 | };
5 |
6 | observable(10)({
7 | next: v => console.log(v, 'next'),
8 | error: e => console.log(e, 'error'),
9 | completed: () => console.log('completed')
10 | });
--------------------------------------------------------------------------------
/src/frp/duality.js:
--------------------------------------------------------------------------------
1 | // When we need to provide a variable for a function
2 | (x => console.log(x * 10, 'provide a variable for a function'))(10);
3 | // When we need provide a function for a variable on demand
4 | (f => { f(10); f(20); f(30) })(x => console.log(x * 10, 'provide a function for a variable'));
--------------------------------------------------------------------------------
/src/frp/reactive.js:
--------------------------------------------------------------------------------
1 | const disposable = (...disposables) => () => disposables.forEach(dispose => dispose());
2 |
3 | const basicObserver = {
4 | next: v => {},
5 | error: e => {},
6 | completed: () => {},
7 | disposed: false
8 | };
9 |
10 | // of :: a -> Observable a
11 | const of = value => ({next, error, completed, disposed} = basicObserver) => (next(value), () => console.log('of disposed!'));
12 |
13 | // fromEvent :: String -> DomElement -> Observable Event
14 | const fromEvent = name => element => ({next, error, completed, disposed} = basicObserver) => {
15 | element.addEventListener(name, (e) => disposed !== true && next(e));
16 |
17 | return () => {
18 | disposed = true;
19 | console.log('fromEvent disposed!');
20 | element.removeEventListener(name, (e) => next(e));
21 | };
22 | };
23 |
24 | // interval :: Number -> Observable Number
25 | const interval = (time) => ({next, error, completed, disposed} = basicObserver) => {
26 | let i = 0;
27 | let id = setInterval(() => {
28 | disposed !== true && next(++i);
29 | }, time);
30 |
31 | return () => {
32 | disposed = true;
33 | console.log('interval disposed!');
34 | clearInterval(id);
35 | };
36 | };
37 |
38 | // map :: (a -> b) -> Observable a -> Observable b
39 | const map = fn => source => ({next, error, completed, disposed} = basicObserver) => source({next: v => next(fn(v))});
40 |
41 | // filter :: (a -> bool) -> Observable a -> Observable a
42 | const filter = predicate => source => o => source({next: v => predicate(v) && o.next(v)});
43 |
44 | // scan :: (b -> a -> b) -> b -> Observable a -> Observable b
45 | const scan = acc => (seed, accumulation = seed) => source => ({next, error, completed, disposed} = basicObserver) => {
46 | return source({next: v => next((accumulation = acc(accumulation, v), accumulation))});
47 | };
48 |
49 | // mergeArray :: [Observable a] -> Observable a
50 | const mergeArray = streams => ({next, error, completed, disposed} = basicObserver) => {
51 | return disposable(...streams.map(stream => {
52 | return stream({next: v => next(v)});
53 | }));
54 | };
55 |
56 | export { basicObserver, disposable, of, interval, fromEvent, map, filter, scan, mergeArray };
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import {Y, B, I} from './combinators';
2 | import {head, tail, length, unapply, apply} from './utils';
3 |
4 | const reduce = f => Y(g => y => xs => length(xs) < 1 ? y : g(f(y)(head(xs)))(tail(xs)));
5 |
6 | const of = (...value) => Array.of(...value) || [...value];
7 |
8 | const empty = () => [];
9 |
10 | const concat = x => y => x.concat(y);
11 |
12 | const flatMap = fn => reduce(acc => next => concat(acc)(fn(next)))(empty());
13 |
14 | const fold = reduce(concat)(empty());
15 |
16 | const foldMap = fn => reduce(acc => next => concat(acc)(fn(next)))(empty());
17 |
18 | const map = fn => flatMap(value => of(fn(value)));
19 |
20 | const filter = predicate => flatMap(value => predicate(value) ? of(value) : empty());
21 |
22 | const ap = fns => source => flatMap(fn => map(value => fn(value))(source))(fns);
23 |
24 | const lift = fn => unapply(reduce(ap)(of(fn)));
25 |
26 | const converge = resultSelector => (...fns) => (...args) => apply(resultSelector)(ap(map(fn => apply(fn))(fns))(of(args)));
27 |
28 | const juxt = apply(converge(unapply(I)));
29 |
30 | const compose = unapply(reduce(B)(I));
31 |
32 | export { reduce, of, flatMap, map, filter, ap, lift, converge, juxt, compose, fold, foldMap, concat };
--------------------------------------------------------------------------------
/src/test.js:
--------------------------------------------------------------------------------
1 | import {of, map, filter, flatMap, ap, lift, converge, juxt, reduce, compose, concat, fold, foldMap} from ".";
2 |
3 | // wrapper
4 | console.log(of(42), 'of');
5 | // concat
6 | console.log(concat([1,2,3])([4]), 'concat');
7 | // map
8 | console.log(map(x => x * 10)([1, 2, 3]), 'map');
9 | // filter
10 | console.log(filter(x => x % 2 === 0)([1, 2, 3]), 'filter');
11 | // flatMap
12 | console.log(flatMap(x => [x * 2, x * 3, x * 4])([1, 2, 3]), 'flatMap');
13 | // applicative
14 | console.log(ap([x => x + 42, x => x * 42])([1,2,3]), 'applicative');
15 | // lift
16 | console.log(lift(x => y => z => x + y + z)([1, 2, 3], [4, 5, 6], [7]), 'lift');
17 | // converge
18 | console.log(converge((x, y) => x * y)((x, y) => x + y, (x, y) => x - y)(3, 4), 'converge');
19 | // juxt
20 | console.log(juxt([Math.max, Math.min])(1, 2, 3, 4, 5), 'juxt');
21 | // reduce
22 | console.log(reduce(acc => next => acc + next)(0)([1,2,3,4,5]), 'reduce');
23 | // compose
24 | console.log(compose(x => x * 10, x => x + 2, (x, y) => x + y)(10, 20), 'compose');
25 | // fold
26 | console.log(fold([[1], [2], [3], [4]]), 'fold');
27 | // foldMap
28 | console.log(foldMap(x => x * 10)([[1], [2], [3], [4]]), 'foldMap');
29 |
30 | // Laws
31 | console.log(map(x => x * 10)([1,2,3]), ap(of(x => x * 10))([1,2,3]));
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | const head = ([head, ...tail]) => head;
2 | const tail = ([head, ...tail]) => tail;
3 | const length = ({length}) => length;
4 |
5 | const _curry = (fn, ...args) => (...innerArgs) => fn(...args, ...innerArgs);
6 |
7 | const curry = (fn, n = fn.length) => {
8 | return (...args) => {
9 | if (args.length < n) {
10 | return n - args.length > 0 ?
11 | curry(_curry(fn, ...args),
12 | n - args.length) :
13 | _curry(fn, ...args);
14 | }
15 | else {
16 | return fn(...args);
17 | }
18 | };
19 | };
20 |
21 | const apply = fn => args => fn(...args);
22 | const unapply = fn => (...args) => fn(args);
23 |
24 | export {
25 | head,
26 | tail,
27 | length,
28 | curry,
29 | apply,
30 | unapply
31 | };
--------------------------------------------------------------------------------