├── .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 | }; --------------------------------------------------------------------------------