├── .gitignore ├── all └── index.js ├── array └── index.js ├── beautiful-fold.js ├── build.js ├── combinators └── index.js ├── compose └── index.js ├── either └── index.js ├── functions └── index.js ├── index.js ├── index2.js ├── list.js ├── maybe └── index.js ├── package.json ├── stream.js ├── sum └── index.js ├── test.js ├── tree.js ├── tree2.js ├── tree3.js └── zip-list.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *.iml 4 | node_modules 5 | -------------------------------------------------------------------------------- /all/index.js: -------------------------------------------------------------------------------- 1 | require('../array'); 2 | const { Maybe, Just, Nothing } = require('../maybe'); 3 | const Type = require('union-type'); 4 | const { I, K, B } = require('../combinators'); 5 | const { zipWith, tail, zip } = require('ramda'); 6 | const { foldMap } = require('..'); 7 | 8 | // Functor 9 | // Pointed Functor 10 | // Apply 11 | // Applicative 12 | // Bind 13 | // Monad 14 | // List 15 | // IO 16 | // Reader 17 | // Writer 18 | // State 19 | // Cont 20 | // Identity 21 | // Maybe 22 | // Error 23 | // Either 24 | // Monad Transformers 25 | // Composition of Applicative, Functor, Traversable, Foldable 26 | // Functor 27 | // Applicative 28 | // Monad 29 | // Monoid 30 | // Foldable 31 | // Traversable 32 | // Alternative 33 | // MonadPlus 34 | // Filterable ? 35 | // Monoids 36 | // Any => Monoid ! 37 | // All => Monoid ! 38 | // Array ? 39 | // Maybe => Monoid, Monad, Applicative, Functor, Foldable, Traversable, Alternative 40 | // IO => Monoid, Monad, Functor, Applicative 41 | // Last => Monoid 42 | // First => Monoid 43 | // Product => Monoid, Functor, Monad, Applicative 44 | // Sum => Monoid, Functor, Monad, Applicative 45 | // Endo => Monoid 46 | // Identity 47 | // Function 48 | // Tuple 49 | // Const 50 | const _Last = Type({ Last: [K(true)] }); 51 | 52 | const { Last } = _Last; 53 | 54 | Last.of = Last; 55 | Last.empty = () => Last(Nothing()); 56 | 57 | Object.assign(_Last.prototype, { 58 | map(f) { 59 | return Last(this.getLast().map(f)); 60 | }, 61 | ap(m) { 62 | return Last(this.getLast().ap(m.getLast())); 63 | }, 64 | chain(f) { 65 | return Last(this.getLast().chain(f)); 66 | }, 67 | foldMap(empty, f) { 68 | return foldMap(empty, f)(this.getLast()); 69 | }, 70 | traverse(of, f) { 71 | return this.getLast().traverse(of, f).map(Last); 72 | }, 73 | concat(m) { 74 | return m.getLast().maybe(this, K(m)); 75 | }, 76 | getLast() { 77 | return this.case({ Last: I }); 78 | } 79 | }); 80 | 81 | // console.log( 82 | // Last(Just(10)).concat(Last(Just(20))), 83 | // Last(Just(10)).concat(Last(Nothing())), 84 | // Last(Nothing()).concat(Last(Just(20))), 85 | // Last(Nothing()).concat(Last(Nothing())) 86 | // ); 87 | 88 | const _First = Type({ First: [K(true)] }); 89 | 90 | const { First } = _First; 91 | 92 | First.of = First; 93 | First.empty = () => First(Nothing()); 94 | 95 | Object.assign(_First.prototype, { 96 | map(f) { 97 | return First(this.getFirst().map(f)); 98 | }, 99 | ap(m) { 100 | return First(this.getFirst().ap(m.getFirst())); 101 | }, 102 | chain(f) { 103 | return First(this.getFirst().chain(f)); 104 | }, 105 | foldMap(empty, f) { 106 | return foldMap(empty, f)(this.getFirst()); 107 | }, 108 | traverse(of, f) { 109 | return this.getFirst().traverse(of, f).map(First); 110 | }, 111 | concat(m) { 112 | return this.getFirst().maybe(m, K(this)); 113 | }, 114 | getFirst() { 115 | return this.case({ First: I }); 116 | } 117 | }); 118 | 119 | const findLast = p => xs => foldMap(Last.empty, x => { 120 | return Last(p(x) ? Just(x) : Nothing()); 121 | })(xs).getLast(); 122 | 123 | // console.log( 124 | // findLast(x => x < 5)([1, 3, 4, 5, 6]), 125 | // foldMap(Array.empty, Array.of)(First(Just(10))) 126 | // ); 127 | 128 | const find = p => xs => foldMap(First.empty, x => { 129 | return First(p(x) ? Just(x) : Nothing()); 130 | })(xs).getFirst(); 131 | 132 | // console.log( 133 | // find(x => x < 5)([1, 3, 4, 5, 6]), 134 | // foldMap(Array.empty, Array.of)(First(Just(10))) 135 | // ); 136 | 137 | const _Product = Type({ Product: [Number] }); 138 | 139 | const { Product } = _Product; 140 | 141 | Product.of = Product; 142 | Product.empty = () => Product(1); 143 | 144 | Object.assign(_Product.prototype, { 145 | // map, chain, foldMap, traverse, ap 146 | concat(m) { 147 | return Product(this.getProduct() + m.getProduct()); 148 | }, 149 | getProduct() { 150 | return this.case({ Product: I }); 151 | } 152 | }); 153 | 154 | const _Any = Type({ Any: [Boolean] }); 155 | 156 | const { Any } = _Any; 157 | 158 | Any.of = Any; 159 | Any.empty = () => Any(false); 160 | 161 | Object.assign(_Any.prototype, { 162 | concat(m) { 163 | return Any(this.getAny() || m.getAny()); 164 | }, 165 | getAny() { 166 | return this.case({ Any: I }); 167 | } 168 | }); 169 | 170 | const or = xs => foldMap(Any.empty, Any)(xs).getAny(); 171 | const any = p => xs => foldMap(Any.empty, B(Any)(p))(xs).getAny(); 172 | 173 | const _All = Type({ All: [Boolean] }); 174 | 175 | const { All } = _All; 176 | 177 | All.of = All; 178 | All.empty = () => All(true); 179 | 180 | Object.assign(_All.prototype, { 181 | concat(m) { 182 | return All(this.getAll() && m.getAll()); 183 | }, 184 | getAll() { 185 | return this.case({ All: I }); 186 | } 187 | }); 188 | 189 | const and = xs => foldMap(All.empty, All)(xs).getAll(); 190 | const all = p => xs => foldMap(All.empty, B(All)(p))(xs).getAll(); 191 | 192 | const compare = (a, b) => a <= b; 193 | const isSorted = xs => B(and)(xs => zipWith(compare, xs, tail(xs)))(xs); 194 | const isSorted_ = xs => all(([a, b]) => compare(a, b))(zip(xs, tail(xs))); 195 | 196 | // console.log(isSorted([4, 5, 6, 7]), isSorted_([4, 5, 6, 7])); 197 | // const isSorted = xs => all(Boolean, zipWith(compare)(xs)(tail(xs))) 198 | 199 | module.exports = { 200 | Last, 201 | First 202 | } 203 | -------------------------------------------------------------------------------- /array/index.js: -------------------------------------------------------------------------------- 1 | const { K } = require('../combinators'); 2 | const { chain_, liftA2, map } = require('../'); 3 | 4 | Array.empty = K([]); 5 | 6 | Object.assign(Array.prototype, { 7 | // Monad 8 | chain(f) { 9 | return [].concat(...map(v => f(v))(this)); 10 | }, 11 | // Applicative 12 | ap(m) { 13 | return chain_(f => map(v => f(v))(m))(this); 14 | }, 15 | // Foldable 16 | foldr(f, z) { 17 | return this.reduceRight((acc, next) => f(next, acc), z); 18 | }, 19 | // Foldable 20 | foldl(f, z) { 21 | return this.reduce((acc, next) => f(acc, next), z); 22 | }, 23 | // Traversable 24 | traverse(of, f) { 25 | return this.foldr((x, ys) => liftA2(a => b => [a, ...b])(f(x))(ys), of([])); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /beautiful-fold.js: -------------------------------------------------------------------------------- 1 | require('./array'); 2 | 3 | const { liftA2, foldr, concat, sequence, map } = require('.') 4 | const { K, B, I } = require('./combinators') 5 | const { Just, Nothing } = require('./maybe') 6 | const { Sum } = require('./sum') 7 | const { Last, First } = require('./all') 8 | 9 | // Basic idea from https://www.stackage.org/haddock/lts-8.5/foldl-1.2.3/Control-Foldl.html 10 | 11 | // data Fold a b = Fold (x -> a -> x) x (x -> b) 12 | const Fold = (step, begin, done) => ({ 13 | step, // step :: (x, a) -> x 14 | begin, // begin :: x 15 | done, // done :: x -> b 16 | map(f) { // Functor 17 | return Fold(step, begin, B(f)(done)) 18 | }, 19 | lmap(f) { // Profunctor 20 | return Fold((x, a) => step(x, f(a)), begin, done) 21 | }, 22 | rmap(f) { // Profunctor 23 | return this.map(f) 24 | }, 25 | concat(m) { // Monoid 26 | return liftA2(concat)(this)(m) 27 | }, 28 | ap({ step:stepR, begin:beginR, done:doneR }) { // Applicative 29 | const newStep = ([x, y], a) => [step(x, a), stepR(y, a)] 30 | const newBegin = [begin, beginR] 31 | const newDone = ([x, y]) => done(x)(doneR(y)) 32 | 33 | return Fold(newStep, newBegin, newDone) 34 | }, 35 | extract() { // Comonad 36 | return done(begin) 37 | }, 38 | duplicate() { // Comonad 39 | return Fold(step, begin, x => Fold(step, x, done)) 40 | }, 41 | fold(as) { // Foldable 42 | return foldr((a, k) => x => k(step(x, a)))(done)(as)(begin) 43 | }, 44 | scan(as) { // Foldable 45 | return foldr((a, k) => x => [].concat(done(x), k(step(x, a))))(x => [done(x)])(as)(begin) 46 | } 47 | }) 48 | 49 | const Fold1_= step => Fold((a, b) => Just(a.maybe(b, x => step(x, b))), Nothing(), I) 50 | 51 | Fold.fold = (f, xs) => f.fold(xs) 52 | Fold.scan = (f, xs) => f.scan(xs) 53 | Fold.empty = T => Fold.of(T.empty()) // Monoid 54 | Fold.of = x => Fold(K(null), null, K(x)) // Pointed Functor 55 | Fold.sum = Fold((x, y) => x + y, 0, I) 56 | Fold.product = Fold((x, y) => x * y, 1, I) 57 | Fold.genericLength = Fold((n, _) => n + 1, 0, I) 58 | Fold.concatAll = T => Fold((a, b) => concat(a)(b), T.empty(), I) 59 | Fold.min = Fold1_((a, b) => Math.min(a, b)) 60 | Fold.max = Fold1_((a, b) => Math.max(a, b)) 61 | Fold.foldMap = (m, f, e) => Fold((x, a) => concat(x)(f(a)), m.empty(), e) 62 | 63 | const range = n => Array.from({length: n}, (_, i) => i + 1) 64 | 65 | const average = liftA2(x => y => x / y)(Fold.sum)(Fold.genericLength) 66 | 67 | console.log( 68 | average.duplicate().fold(range(10000)).extract(), 69 | average.fold(range(15000)), 70 | Fold.sum.scan(range(10)), 71 | Fold.genericLength.fold([1,2,3,4]), 72 | Fold.sum.fold([1,2,3,4]), 73 | Fold.concatAll(Sum).lmap(Sum).fold([1,2,3,4]).getSum(), 74 | Fold.concatAll(Sum).fold(map(Sum)([1,2,3,4])).getSum(), 75 | Fold.fold(Fold.sum, [1,2,3,4]), 76 | Fold.fold(sequence(Fold.of, [1,2,3,4,5].map(x => Fold.sum.lmap(y => Math.pow(y, x)))), [1,2,3,4,5,6,7,8,9,10]), 77 | [...Fold.fold(liftA2(x => y => [x, y])(Fold.min)(Fold.max), range(10000)).map(x => x.fromJust())], 78 | [...Fold.fold(liftA2(x => y => [x, y])(Fold.sum)(Fold.product), range(100))], 79 | liftA2(x => y => x / y) 80 | (Fold((x, y) => concat(x)(Sum(y)), Sum.empty(), x => x.getSum())) 81 | (Fold((x, y) => concat(Sum(1))(Sum(y)), Sum.empty(), x => x.getSum())).fold(range(10000)), 82 | liftA2(x => y => x / y) 83 | (Fold.foldMap(Sum, Sum, x => x.getSum())) 84 | (Fold.foldMap(Sum, K(Sum(1)), x => x.getSum())).fold([1,2,3,4,5]), 85 | liftA2(x => y => [x, y]) 86 | (Fold.foldMap(First, B(First)(Just), x => x.getFirst())) 87 | (Fold.foldMap(Last, B(Last)(Just), x => x.getLast())) 88 | .fold([]) 89 | .map(x => x.getOrElse(void 0)) 90 | ) 91 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | const cons = a => b => [a, ...b]; 2 | const build = g => g(cons)([]); 3 | const foldr = f => n => xs => xs.reduceRight((acc, next) => f(next)(acc), n); 4 | const compose = f => g => x => f(g(x)); 5 | 6 | Function.prototype.map = function(f) { 7 | return compose(f)(this); 8 | }; 9 | 10 | const map = f => xs => build(c => n => foldr(f.map(c))(n)(xs)); 11 | 12 | // const map = f => xs => build(c => n => xs.reduceRight((acc, next) => c(f(next))(acc), n)); 13 | 14 | console.log(map(x => x * 10)([10, 20, 30])); 15 | 16 | const concatMap = f => xs => build(c => n => foldr(f.map(foldr(c)))(n)(xs)); 17 | 18 | // const concatMap = f => xs => build(c => n => xs.reduceRight((b, x) => f(x).reduceRight((a, y) => c(y)(a) ,b) ,n)); 19 | 20 | console.log(concatMap(x => [x, x * x, x * x * x])([10, 20, 30])); 21 | -------------------------------------------------------------------------------- /combinators/index.js: -------------------------------------------------------------------------------- 1 | // K :: a -> b -> a 2 | const K = x => y => x; 3 | // S :: (a -> b -> c) -> (a -> b) -> a -> c 4 | const S = x => y => z => x(z)(y(z)); 5 | // I :: a -> a 6 | const I = S(K)(K); 7 | // I_ :: (a -> b) -> a -> b 8 | const I_ = S(S(K)); 9 | // B :: (b -> c) -> (a -> b) -> a -> c 10 | const B = S(K(S))(K); 11 | // C :: (a -> b -> c) -> b -> a -> c 12 | const C = S(B(B)(S))(K(K)); 13 | // W :: (a -> a -> b) -> a -> b 14 | const W = S(S)(S(K)); 15 | // W__ :: (a -> b -> c -> c -> d) -> a -> b -> c -> d 16 | const W__ = B(B(W)); 17 | // Q :: (a -> b) -> (b -> c) -> a -> c 18 | const Q = C(B); 19 | 20 | module.exports = { S, K, I, B, C, W, W__, Q }; 21 | -------------------------------------------------------------------------------- /compose/index.js: -------------------------------------------------------------------------------- 1 | const Type = require('union-type'); 2 | const { K, I, B } = require('../combinators'); 3 | const { liftA2, map, ap, foldMap, traverse } = require('../'); 4 | 5 | const _Compose = Type({ 6 | Compose: [K(true)] 7 | }); 8 | 9 | const { Compose } = _Compose; 10 | 11 | // Pointed 12 | Compose.of = (T1, T2, x) => Compose(T1.of(T2.of(x))); 13 | // Monoid, Alternative 14 | Compose.empty = T => Compose(T.empty()); 15 | 16 | Object.assign(_Compose.prototype, { 17 | // Alternative 18 | alt(c) { 19 | return Compose(this.getCompose().alt(c.getCompose())); 20 | }, 21 | // Functor 22 | map(f) { 23 | return Compose(B(map)(map)(f)(this.getCompose())); 24 | }, 25 | // Applicative 26 | ap(m) { 27 | return Compose(liftA2(ap)(this.getCompose())(m.getCompose())); 28 | }, 29 | // Traversable 30 | traverse(of, f) { 31 | return map(x => Compose(x))( 32 | traverse(of, traverse(of, f))(this.getCompose()) 33 | ); 34 | }, 35 | // Foldable 36 | foldMap(empty, f) { 37 | return foldMap(empty, foldMap(empty, f))(this.getCompose()); 38 | }, 39 | getCompose() { 40 | return this.case({ Compose: I }); 41 | } 42 | }); 43 | 44 | module.exports = { Compose }; 45 | 46 | require('../functions'); 47 | 48 | const take = n => xs => xs.slice(0, n); 49 | const drop = n => xs => xs.slice(n); 50 | 51 | const rotate = liftA2(a => b => a.concat(b))(Compose(drop))( 52 | Compose(take) 53 | ).getCompose(); 54 | 55 | console.log(rotate(3)([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); 56 | -------------------------------------------------------------------------------- /either/index.js: -------------------------------------------------------------------------------- 1 | const Type = require('union-type'); 2 | const { K, B, I } = require('../combinators'); 3 | const { map, chain_ } = require('../'); 4 | 5 | const Either = Type({ 6 | Left: [K(true)], 7 | Right: [K(true)] 8 | }); 9 | 10 | const { Left, Right } = Either; 11 | 12 | Either.of = Right; 13 | 14 | Either.try = f => (...args) => { 15 | try { 16 | return Right(f(...args)); 17 | } catch (e) { 18 | return Left(e); 19 | } 20 | }; 21 | 22 | Object.assign(Either.prototype, { 23 | // Functor 24 | map(f) { 25 | return chain_(B(Right)(f))(this); 26 | }, 27 | // Bifunctor 28 | bimap(f, g) { 29 | return this.either(B(Left)(f), B(Right)(g)); 30 | }, 31 | // Monad 32 | chain(f) { 33 | return this.either(Left, f); 34 | }, 35 | // Applicative 36 | ap(m) { 37 | return chain_(f => map(v => f(v))(m))(this); 38 | }, 39 | // Foldable 40 | foldr(f, z) { 41 | return this.either(K(z), y => f(y, z)); 42 | }, 43 | // Foldable 44 | foldl(f, z) { 45 | return this.either(K(z), y => f(z, y)); 46 | }, 47 | // Foldable 48 | foldMap(empty, f) { 49 | return this.either(K(empty()), f); 50 | }, 51 | // Traversable 52 | traverse(of, f) { 53 | return this.either(x => of(Left(x)), B(map(Right))(f)); 54 | }, 55 | fromLeft() { 56 | return this.case({ Left: I }); 57 | }, 58 | fromRight() { 59 | return this.case({ Right: I }); 60 | }, 61 | isLeft() { 62 | return this.either(K(true), K(false)); 63 | }, 64 | isRight() { 65 | return !this.isLeft(); 66 | }, 67 | either(f, g) { 68 | return this.case({ 69 | Left: _ => f(_), 70 | Right: _ => g(_) 71 | }); 72 | } 73 | }); 74 | 75 | const either = f => g => e => e.either(f, g); 76 | 77 | // Either.try(x => { 78 | // if (x === 2) { 79 | // throw new Error('This is 2'); 80 | // } else { 81 | // return x * x; 82 | // } 83 | // })(2).either(x => console.log(`Error => ${x.message}`), x => console.log(x)); 84 | 85 | module.exports = { Either, Left, Right, either }; 86 | -------------------------------------------------------------------------------- /functions/index.js: -------------------------------------------------------------------------------- 1 | const { S, B, K, W__, Q } = require('../combinators'); 2 | const { liftA2, concat } = require('../'); 3 | 4 | const T = require('union-type'); 5 | 6 | const _Fn = T({ 7 | Fn: [Function] 8 | }); 9 | 10 | const { Fn } = _Fn; 11 | 12 | Fn.of = B(Fn)(K); 13 | Fn.empty = T => K(T.empty()); 14 | 15 | Object.assign(_Fn.prototype, { 16 | runFn(x) { 17 | return this.case({ 18 | Fn(fn) { 19 | return fn(x); 20 | } 21 | }); 22 | }, 23 | map(f) { 24 | return Fn(x => f(this.runFn(x))); 25 | }, 26 | chain(f) { 27 | return Fn(x => f(this.runFn(x)).runFn(x)); 28 | } 29 | }); 30 | 31 | console.log( 32 | 'test', 33 | Fn(x => x * 10) 34 | .map(x => x * 20) 35 | .map(x => x * 40) 36 | .chain(x => Fn(y => x * y)) 37 | .runFn(10) 38 | ); 39 | 40 | // Pointed 41 | Function.of = K; 42 | // Monoid 43 | Function.empty = T => K(T.empty()); 44 | 45 | Object.assign(Function.prototype, { 46 | // Functor 47 | map(f) { 48 | return B(f)(this); 49 | }, 50 | // Applicative 51 | ap(m) { 52 | return S(this)(m); 53 | }, 54 | // Monad 55 | chain(f) { 56 | return W__(Q)(this)(f); 57 | }, 58 | // Monoid 59 | concat(m) { 60 | return liftA2(concat)(this)(m); 61 | } 62 | }); 63 | 64 | console.log((x => x * 10).map(x => x - 20)(10)); 65 | console.log(B(x => x - 20)(x => x * 10)(10)); 66 | 67 | const take = n => xs => xs.slice(0, n); 68 | const drop = n => xs => xs.slice(n); 69 | 70 | const rotate = concat(drop)(take); 71 | 72 | Number.prototype[Symbol.iterator] = function*() { 73 | for (let i = 0; i < this; i++) { 74 | yield i; 75 | } 76 | }; 77 | 78 | for (let i of 10) { 79 | console.log(i); 80 | } 81 | 82 | console.log( 83 | rotate(3)([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 84 | [...10], 85 | Array.from(10) 86 | ); 87 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { B, K, C, I } = require('./combinators'); 2 | 3 | // pure, return 4 | const of = T => value => T.of(value); 5 | // map, fmap, <$>, liftM, liftA 6 | const map = f => source => { 7 | if (source.map) return source.map(v => f(v)); 8 | if (source.ap) return ap(source.constructor.of(f))(source); 9 | if (source.chain) return chain_(B(source.constructor.of)(f))(source); 10 | }; 11 | // <&> 12 | const flippedMap = C(map); 13 | // <$ 14 | const mapLeft = B(map)(K); 15 | // $> 16 | // const mapRight = xs => value => map(K(value))(xs); 17 | const mapRight = C(mapLeft); 18 | // <*>, ap 19 | const ap = source => m => { 20 | if (source.ap) return source.ap(m); 21 | if (source.chain) return chain_(f => map(v => f(v))(m))(source); 22 | }; 23 | // liftA, liftM 24 | const liftA = f => x => ap(x.constructor.of(f))(x); 25 | // liftA2, liftM2 26 | const liftA2 = f => x => y => x.map(f).ap(y); 27 | // *>, >> 28 | const apRight = liftA2(K(I)); 29 | // <* 30 | const apLeft = liftA2(K); 31 | // <**> 32 | const flippedAp = liftA2(C(I)); 33 | // >>= 34 | const chain = source => f => source.chain(f); 35 | // =<< 36 | const chain_ = C(chain); 37 | // >>, *> 38 | const then = m => chain_(K(m)); 39 | // join 40 | const join = xss => chain_(I)(xss); 41 | // mappend 42 | const concat = x => y => x.concat(y); 43 | // mconcat 44 | const concatAll = (empty, xs) => { 45 | if (xs.concatAll) return xs.concatAll(T); 46 | if (xs.foldr) return xs.foldr((x, acc) => x.concat(acc), empty()); 47 | }; 48 | // foldr 49 | const foldr = f => z => xs => xs.foldr(f, z); 50 | // foldl 51 | const foldl = f => z => xs => xs.foldl(f, z); 52 | // foldMap 53 | const foldMap = (empty, f) => xs => { 54 | if (xs.foldMap) return xs.foldMap(empty, f); 55 | if (xs.foldr) return foldr((x, acc) => f(x).concat(acc))(empty())(xs); 56 | }; 57 | // fold 58 | const fold = (empty, xs) => { 59 | if (xs.fold) return xs.fold(empty); 60 | if (xs.foldMap) return xs.foldMap(empty, I); 61 | }; 62 | // liftA, map 63 | const liftM = f => x => x.chain(_ => x.constructor.of(f(_))); 64 | // liftA2, liftM2 65 | const liftM2 = f => 66 | x => y => x.chain(a => y.chain(b => x.constructor.of(f(a)(b)))); 67 | // ap, <*> 68 | const apM = x => y => x.chain(f => y.chain(v => x.constructor.of(f(v)))); 69 | // Foldable 70 | const traverse_ = (of, f) => foldr((x, acc) => f(x).apR(acc))(of(void 0)); 71 | const for_ = xs => (of, f) => traverse_(of, f)(xs); 72 | const mapM_ = (of, f) => foldr((x, acc) => f(x).then(acc))(of(void 0)); 73 | const forM_ = xs => (of, f) => mapM_(of, f)(xs); 74 | const sequenceA_ = (of, f) => foldr((x, acc) => x.apR(acc))(of(void 0)); 75 | const sequence_ = (of, f) => foldr((x, acc) => x.then(acc))(of(void 0)); 76 | // Traversable 77 | const traverse = (of, f) => xs => xs.traverse(of, f); 78 | const sequence = (of, xs) => { 79 | if (xs.sequence) return xs.sequence(of); 80 | if (xs.traverse) return xs.traverse(of, I); 81 | }; 82 | const mapM = traverse; 83 | const _for = xs => (of, f) => traverse(of, f); 84 | const forM = xs => (of, f) => mapM(of, f); 85 | 86 | const foldrM = of => (f, z, xs) => { 87 | const f_ = (k, x) => z => chain_(k)(f(x, z)); 88 | return foldl(f_)(of)(xs)(z); 89 | }; 90 | 91 | const foldlM = of => (f, z, xs) => { 92 | const f_ = (x, k) => z => chain_(k)(f(z, x)); 93 | return foldr(f_)(of)(xs)(z); 94 | }; 95 | 96 | const foldM = foldlM; 97 | 98 | const foldM_ = of => (f, z, xs) => then(of(void 0))(foldrM(of)(f, z, xs)); 99 | const replicate = (n, v) => Array.from(Array(n), K(v)); 100 | const replicateM = (of, n) => v => sequence(of, replicate(n, v)); 101 | 102 | module.exports = { 103 | of, 104 | map, 105 | chain, 106 | chain_, 107 | ap, 108 | apRight, 109 | apLeft, 110 | mapRight, 111 | liftA, 112 | liftM, 113 | liftA2, 114 | liftM2, 115 | flippedAp, 116 | then, 117 | join, 118 | concat, 119 | concatAll, 120 | foldr, 121 | foldl, 122 | foldMap, 123 | fold, 124 | apM, 125 | traverse_, 126 | for_, 127 | mapM_, 128 | forM_, 129 | sequenceA_, 130 | sequence_, 131 | traverse, 132 | sequence, 133 | mapM, 134 | _for, 135 | forM, 136 | foldrM, 137 | foldlM, 138 | foldM, 139 | foldM_, 140 | replicate, 141 | replicateM, 142 | mapLeft 143 | }; 144 | -------------------------------------------------------------------------------- /index2.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const Task = require('data.task'); 3 | const Either = require('data.either'); 4 | const { List } = require('immutable-ext'); 5 | 6 | const Sum = x => ({ 7 | x, 8 | concat: ({ x: y }) => Sum(x + y), 9 | inspect: () => `Sum(${x})` 10 | }); 11 | 12 | Sum.empty = () => Sum(0); 13 | 14 | const Pair = (x, y) => ({ 15 | x, 16 | y, 17 | bimap: (f, g) => Pair(f(x), g(y)), 18 | toList: () => [x, y], 19 | concat: ({ x: x1, y: y1 }) => Pair(x.concat(x1), y.concat(y1)), 20 | inspect: () => `Pair(${x}, ${y})` 21 | }); 22 | 23 | const httpGet = url => 24 | new Task((rej, res) => 25 | request(url, (error, response, body) => error ? rej(error) : res(body))); 26 | 27 | const getJSON = url => httpGet(url).map(parse).chain(eitherToTask); 28 | 29 | const first = xs => Either.fromNullable(xs[0]); 30 | 31 | const eitherToTask = e => e.fold(Task.rejected, Task.of); 32 | 33 | const parse = Either.try(JSON.parse); 34 | 35 | const findArtist = name => 36 | getJSON(`https://api.spotify.com/v1/search?q=${name}&type=artist`) 37 | .map(result => result.artists.items) 38 | .map(first) 39 | .chain(eitherToTask); 40 | 41 | const relatedArtists = id => 42 | getJSON(`https://api.spotify.com/v1/artists/${id}/related-artists`).map( 43 | result => result.artists 44 | ); 45 | 46 | const Intersection = xs => ({ 47 | xs, 48 | concat: ({ xs: ys }) => Intersection(xs.filter(x => ys.some(y => x === y))) 49 | }); 50 | 51 | const related = name => findArtist(name) 52 | .map(artist => artist.id) 53 | .chain(relatedArtists) 54 | .map(artists => artists.map(artist => artist.name)); 55 | 56 | const artistIntersection = rels => rels 57 | .foldMap(x => Pair(Intersection(x), Sum(x.length))) 58 | .bimap(x => x.xs, y => y.x) 59 | .toList(); 60 | 61 | const main = names => 62 | List(names).traverse(Task.of, related).map(artistIntersection); 63 | 64 | Task 65 | .of(['oasis', 'blur', 'radiohead']) 66 | .chain(main) 67 | .fork(console.error, console.log); 68 | -------------------------------------------------------------------------------- /list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const T = require('union-type'); 3 | 4 | const { K, C, B, I } = require('./combinators'); 5 | require('./array'); 6 | const { Sum } = require('./sum'); 7 | const { Just, Nothing, Maybe } = require('./maybe'); 8 | const { chain_, liftA2, map } = require('.'); 9 | 10 | const foldMap = (empty, f) => xs => { 11 | if (xs.foldMap) return xs.foldMap(empty, f); 12 | if (xs.foldr) return xs.foldr(x => acc => f(x).concat(acc), empty()); 13 | }; 14 | 15 | // data List a = Nil | Cons a (List a) 16 | var List = T({ Nil: [], Cons: [K(true), List] }); 17 | 18 | /* 19 | l = 1 + x * l 20 | l - x * l = 1 21 | l * (1 - x) = 1 22 | l = 1 / (1 - x) 23 | */ 24 | const { Nil, Cons } = List; 25 | 26 | List.of = v => Cons(v, Nil()); 27 | List.empty = Nil; 28 | 29 | Object.assign(Array.prototype, { 30 | toList() { 31 | return build(c => n => this.reduceRight((e, a) => c(a)(e), n)); 32 | // if(this.length > 0) { 33 | // const [head, ...tail] = this; 34 | // return Cons(head, tail.toList()) 35 | // } else { 36 | // return Nil() 37 | // } 38 | } 39 | }); 40 | 41 | // build (\c n -> foldr (\x y -> foldr c y x) n xs) 42 | const build = g => g(Cons)(Nil()); 43 | // const concat = xss => build(c => n => xss.foldr(x => y => x.foldr(c, y), n)) 44 | // const concatAll = xss => xss.foldr(x => y => x.foldr(Cons, y), Nil()) 45 | const concatAll = xss => xss.foldr(x => y => x.concat(y), Nil()); 46 | // const concat = xss => xss.foldr(n => a => { 47 | // return n.append(a); 48 | // }, Nil()) 49 | Object.assign(List.prototype, { 50 | // toString() { 51 | // return this.case({ 52 | // Nil: () => 'Nil()', 53 | // Cons: (head, tail) => `Cons(${head.toString()}, ${tail.toString()})` 54 | // }); 55 | // }, 56 | length() { 57 | return this.case({ 58 | Nil: K(0), 59 | Cons(x, xs) { 60 | return 1 + xs.length(); 61 | } 62 | }); 63 | }, 64 | at(n) { 65 | return this.case({ 66 | Cons(x, xs) { 67 | if (n === 0) return x; 68 | return xs.at(n - 1); 69 | } 70 | }); 71 | }, 72 | head() { 73 | return this.case({ Cons: (x, _) => x }); 74 | }, 75 | tail() { 76 | return this.case({ Cons: (_, xs) => xs }); 77 | }, 78 | toString() { 79 | return this.case({ 80 | Nil: K('Nil'), 81 | Cons: (head, tail) => `${head.toString()} : ${tail.toString()}` 82 | }); 83 | }, 84 | cons(v) { 85 | return Cons(v, this); 86 | }, 87 | map(f) { 88 | return this.case({ Nil, Cons: (head, tail) => Cons(f(head), tail.map(f)) }); 89 | }, 90 | filter(p) { 91 | return this.case({ 92 | Nil, 93 | Cons: (head, tail) => 94 | p(head) ? Cons(head, tail.filter(p)) : tail.filter(p) 95 | }); 96 | }, 97 | chain(f) { 98 | return foldMap(List.empty, f)(this); 99 | //return concatAll(this.map(v => f(v))) 100 | }, 101 | ap(m) { 102 | return this.chain(f => m.map(v => f(v))); 103 | }, 104 | foldr(f, z) { 105 | return this.case({ Nil: K(z), Cons: (x, xs) => f(x)(xs.foldr(f, z)) }); 106 | }, 107 | foldl(f, z) { 108 | return this.case({ Nil: K(z), Cons: (x, xs) => xs.foldl(f, f(z)(x)) }); 109 | }, 110 | concat(ys) { 111 | return this.case({ Nil: K(ys), Cons: (x, xs) => Cons(x, xs.concat(ys)) }); 112 | }, 113 | toArray() { 114 | return this.case({ 115 | Nil: K([]), 116 | Cons: (head, tail) => [head, ...tail.toArray()] 117 | }); 118 | }, 119 | traverse(of, f) { 120 | return this.foldr( 121 | x => ys => liftA2(a => b => Cons(a, b))(f(x))(ys), 122 | of(Nil()) 123 | ); 124 | }, 125 | isNil() { 126 | return this.case({ Nil: K(true), Cons: K(false) }); 127 | }, 128 | *[Symbol.iterator]() { 129 | if (this.isNil()) { 130 | return; 131 | } else { 132 | yield this[0]; 133 | yield* this[1]; 134 | } 135 | } 136 | }); 137 | 138 | function* reduce(fn, init, xs) { 139 | let acc = init; 140 | for (const x of xs) { 141 | acc = fn(acc, x); 142 | // yield acc; 143 | } 144 | yield acc; 145 | } 146 | 147 | // console.log( 148 | // ...reduce((acc, x) => acc.concat(x), [], new Map([[10, 20], [30, 40]])) 149 | // ) 150 | for (const x of Cons( 151 | [...Cons(10, Cons(20, Nil()))], 152 | Cons( 153 | [...Cons(30, Cons(40, Nil()))], 154 | Cons([...Cons(50, Cons(60, Nil()))], Nil()) 155 | ) 156 | )) { 157 | console.log(x); 158 | } 159 | 160 | // take :: Number -> Iterator a -> Iterator a 161 | function* take(n, xs) { 162 | for (let x of xs) { 163 | if (n-- === 0) return; 164 | yield x; 165 | } 166 | } 167 | 168 | // cycle :: Iterator a -> Iterator a 169 | function* cycle(xs) { 170 | yield* xs; 171 | yield* cycle(xs); 172 | } 173 | 174 | const compose = l => l.foldr(B, I); 175 | 176 | console.log( 177 | 'compose', 178 | compose(Cons(x => x * 10, Cons(x => x - 20, Nil())))(10) 179 | ); 180 | 181 | console.log( 182 | [...take(10, cycle(Cons(10, Cons(20, Cons(30, Nil())))))].toList().toString() 183 | ); 184 | 185 | const zip = xs => ys => { 186 | if (xs.length() > 0 && ys.length() > 0) { 187 | return Cons( 188 | Cons(xs.head(), Cons(ys.head(), Nil())), 189 | zip(xs.tail())(ys.tail()) 190 | ); 191 | } else { 192 | return Nil(); 193 | } 194 | }; 195 | 196 | console.log( 197 | zip(Cons(10, Cons(20, Nil())))(Cons(30, Cons(40, Nil()))) 198 | .map(x => x.toArray()) 199 | .toArray() 200 | ); 201 | 202 | const Y = f => (x => f(v => x(x)(v)))(x => f(v => x(x)(v))); 203 | 204 | // quicksort :: List a -> List a 205 | const quicksort = Y(f => l => { 206 | return l.case({ 207 | Nil, 208 | Cons(x, xs) { 209 | const bigger = f(xs.filter(_ => _ > x)); 210 | const smaller = f(xs.filter(_ => _ <= x)); 211 | 212 | //return [...smaller, x, ...bigger] 213 | return smaller.concat(Cons(x, Nil()).concat(bigger)); 214 | } 215 | }); 216 | // if(l.length() === 0) { 217 | // return Nil() 218 | // } else { 219 | // const x = l.head() 220 | // const xs = l.tail() 221 | // 222 | // const bigger = f(xs.filter(_ => _ > x)) 223 | // const smaller = f(xs.filter(_ => _ <= x)) 224 | // 225 | // return smaller.concat(Cons(x, Nil()).concat(bigger)) 226 | // } 227 | }); 228 | 229 | // var r = (function f(n){ 230 | // if (n <= 0) { 231 | // return "foo"; 232 | // } 233 | // return f(n - 1); 234 | // }(1e6)) 235 | // 236 | // "use strict"; 237 | // function f(n){ 238 | // if (n <= 0) { 239 | // return "foo"; 240 | // } 241 | // return g(n - 1); 242 | // } 243 | // function g(n){ 244 | // if (n <= 0) { 245 | // return "bar"; 246 | // } 247 | // return f(n - 1); 248 | // } 249 | // return f(1e6) === "foo" && f(1e6+1) === "bar"; 250 | console.log( 251 | [...Cons(1, Cons(2, Cons(3, Nil()))).filter(x => x % 2 === 0)], 252 | [...quicksort(Cons(200, Cons(3, Cons(1, Nil()))))], 253 | // r === "foo" 254 | [...take(10, cycle(quicksort(Cons(200, Cons(3, Cons(1, Nil()))))))].toList() 255 | ); 256 | // console.log( 257 | // C(Cons)(Nil())(10) 258 | // ) 259 | // var List2 = T({ 260 | // Nil: [], 261 | // [':']: [K(true), List2] 262 | // }) 263 | // 264 | // Object.assign(List2.prototype, { 265 | // [':'](x, xs) { 266 | // return List2[':'](x, xs) 267 | // } 268 | // }) 269 | // 270 | // console.log( 271 | // List2[':'](10, List2[':'](20, List2.Nil())) 272 | // ) 273 | // console.log( 274 | // //foldMap(Sum.empty, Sum) 275 | // (Cons(10, Cons(20, Nil()))) 276 | // //.getSum() 277 | // .chain(x => Cons(x * x, Cons(x * x * x, Nil()))) 278 | // .toArray() 279 | // // .toList() 280 | // // .toArray() 281 | // // Cons(Just(10), Cons(Just(20), Cons(Just(30), Cons(Just(40), Nil())))) 282 | // // .traverse(Maybe.of, x => x) 283 | // // .map(x => x.map(_ => _ + 10)) 284 | // // .toString() 285 | // // Cons(10, Cons(20, Nil())) 286 | // // .map(x => y => Cons(x, Cons(y, Nil()))) 287 | // // .ap(Cons(30, Cons(40, Nil()))).foldr(n => a => [n.toArray(), ...a] ,[]) 288 | // // Cons(10, Cons(20, Nil())).chain(x => Cons(x * x, Cons(x * x * x, Nil()))) 289 | // // .toString() 290 | // // concat(Cons( 291 | // // Cons(10, Cons(20, Nil())), 292 | // // Cons( 293 | // // Cons(30, Cons(40, Nil())), 294 | // // Cons( 295 | // // Cons(50, Cons(60, Nil())), 296 | // // Nil()) 297 | // // ) 298 | // // )).toString() 299 | // // Cons( 300 | // // Cons(10, Cons(20, Nil())), 301 | // // Cons(Cons(30, Cons(40, Nil())), Nil()), 302 | // // ) 303 | // // [10,20,30,40].map(x => x * 10).toList().cons(100).toString() 304 | // // Cons(10, Cons(20, Cons(30, Nil()))).map(x => x * 10).toArray() 305 | // ) 306 | -------------------------------------------------------------------------------- /maybe/index.js: -------------------------------------------------------------------------------- 1 | const Type = require('union-type'); 2 | const { K, B, I } = require('../combinators'); 3 | const { map } = require('..'); 4 | 5 | // data Maybe a = Nothing | Just a 6 | const Maybe = Type({ 7 | Nothing: [], 8 | Just: [K(true)] 9 | }); 10 | 11 | const { Just, Nothing } = Maybe; 12 | 13 | // Pointed 14 | Maybe.of = Just; 15 | // Monoid 16 | Maybe.empty = Nothing; 17 | 18 | Object.assign(Maybe.prototype, { 19 | // Monad 20 | chain(f) { 21 | return this.maybe(Nothing(), f); 22 | }, 23 | // Functor 24 | map(f) { 25 | return this.chain(B(v => Maybe.of(v))(f)); 26 | }, 27 | // Applicative 28 | ap(m) { 29 | return this.chain(f => m.map(v => f(v))); 30 | }, 31 | // Foldable 32 | foldr(f, z) { 33 | return this.maybe(z, x => f(x, z)); 34 | }, 35 | // Foldable 36 | foldl(f, z) { 37 | return this.maybe(z, x => f(z, x)); 38 | }, 39 | // Traversable 40 | traverse(of, f) { 41 | return this.maybe(of(Nothing()), B(map(v => Maybe.of(v)))(f)); 42 | }, 43 | // Monoid 44 | concat(m) { 45 | return this.isNothing() 46 | ? m 47 | : m.isNothing() ? this : this.fromJust().concat(m.fromJust()); 48 | }, 49 | fromJust() { 50 | return this.case({ Just: I }); 51 | }, 52 | isJust() { 53 | return this.maybe(false, K(true)); 54 | }, 55 | isNothing() { 56 | return !this.isJust(); 57 | }, 58 | getOrElse(d) { 59 | return this.maybe(d, I); 60 | }, 61 | maybe(d, f) { 62 | return this.case({ Just: v => f(v), Nothing: _ => d }); 63 | }, 64 | valueOf() { 65 | return this.fromJust(); 66 | }, 67 | toString() { 68 | return this.case({ 69 | Nothing: _ => `Nothing`, 70 | Just: v => `Just ${v}` 71 | }); 72 | } 73 | }); 74 | 75 | module.exports = { Maybe, Just, Nothing }; 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "practical-functional-programming", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/xgrommx/practical-functional-programming.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/xgrommx/practical-functional-programming/issues" 18 | }, 19 | "homepage": "https://github.com/xgrommx/practical-functional-programming#readme", 20 | "dependencies": { 21 | "rx": "^4.1.0", 22 | "union-type": "^0.3.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stream.js: -------------------------------------------------------------------------------- 1 | const T = require('union-type'); 2 | 3 | const { map } = require('.'); 4 | const { K, I, B } = require('./combinators'); 5 | const { Either, Left, Right, either } = require('./either'); 6 | 7 | const _Stream = T({ 8 | Stream: { runStream: Function /* v => Either v*/ } 9 | }); 10 | 11 | const { Stream } = _Stream; 12 | 13 | Stream.of = v => Stream(sink => { 14 | B(sink)(Either.try(I))(v); 15 | return () => {}; 16 | }); 17 | 18 | Stream.interval = time => Stream(sink => { 19 | let i = 0; 20 | const id = setInterval(() => sink(Right((i++))), time); 21 | 22 | return () => clearInterval(id); 23 | }); 24 | 25 | Object.assign(_Stream.prototype, { 26 | bimap(f, g) { 27 | return Stream(sink => { 28 | return this.runStream(B(sink)(either(B(Left)(f))(Either.try(g)))); 29 | }); 30 | }, 31 | map(f) { 32 | return this.bimap(I, f); 33 | } 34 | }); 35 | 36 | const disposable = Stream 37 | .interval(1000) 38 | .map(x => { 39 | if (x % 2 === 0) throw new Error('${x} % 2 === 0'); 40 | return x * 10; 41 | }) 42 | .runStream(e => { 43 | e.either(e => console.log(`Error => ${e.message}`), v => 44 | console.log(`Value => ${v}`)); 45 | }); 46 | 47 | setTimeout(() => disposable(), 5000); 48 | -------------------------------------------------------------------------------- /sum/index.js: -------------------------------------------------------------------------------- 1 | const Type = require('union-type'); 2 | const { I, K } = require('../combinators'); 3 | 4 | const _Sum = Type({ 5 | Sum: [K(true)] 6 | }); 7 | 8 | const { Sum } = _Sum; 9 | 10 | Sum.of = Sum; 11 | Sum.empty = () => Sum(0); 12 | 13 | Object.assign(_Sum.prototype, { 14 | concat(m) { 15 | return Sum(this.getSum() + m.getSum()); 16 | }, 17 | getSum() { 18 | return this.case({ Sum: I }); 19 | } 20 | }); 21 | 22 | module.exports = { 23 | Sum 24 | }; 25 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | require('./array'); 2 | require('./functions'); 3 | 4 | const { K, B, I } = require('./combinators'); 5 | const { Maybe, Just, Nothing } = require('./maybe'); 6 | const { Either, Left, Right } = require('./either'); 7 | const { Compose } = require('./compose'); 8 | 9 | const { foldMap, replicateM, mapLeft, mapRight, traverse, liftA2 } = require( 10 | '.' 11 | ); 12 | 13 | console.log( 14 | // foldMap(Array, Array.of)(Compose([Just(10), Just(20), Nothing()])), 15 | // replicateM(Array, 2)([1, 2, 3]), 16 | mapRight([1, 2, 3])([4]), 17 | mapLeft([1, 2, 3])([4]) 18 | ); 19 | 20 | const Type = require('union-type'); 21 | 22 | const _Identity = Type({ 23 | Identity: [K(true)] 24 | }); 25 | 26 | const { Identity } = _Identity; 27 | 28 | Identity.of = Identity; 29 | 30 | Object.assign(_Identity.prototype, { 31 | getIdentity() { 32 | return this.case({ Identity: I }); 33 | }, 34 | map(f) { 35 | return Identity(f(this.getIdentity())); 36 | }, 37 | ap(m) { 38 | return Identity(this.getIdentity()(m.getIdentity())); 39 | }, 40 | foldMap(f) { 41 | return f(this.getIdentity()); 42 | }, 43 | traverse(f) { 44 | return f(this.getIdentity()).map(x => Identity(x)); 45 | }, 46 | chain(f) { 47 | return f(this.runIdentity()); 48 | } 49 | }); 50 | 51 | const _Const = Type({ 52 | Const: [K(true)] 53 | }); 54 | 55 | const { Const } = _Const; 56 | 57 | Const.of = Const; 58 | 59 | Object.assign(_Const.prototype, { 60 | getConst() { 61 | return this.case({ Const: (a, b) => a }); 62 | }, 63 | map() { 64 | return this; 65 | }, 66 | ap(m) { 67 | return Const(this.getConst().concat(m.getConst())); 68 | } 69 | }); 70 | 71 | const foldMapDefault = (T, f) => 72 | xs => traverse(K(Const.of(T.empty())), B(Const)(f))(xs).getConst(); 73 | 74 | console.log( 75 | foldMapDefault(Array, Array.of)(Compose([Just(10), Just(20), Nothing()])) 76 | ); 77 | 78 | const fmapDefault = (T, f) => 79 | xs => traverse(K(Identity.of(T.empty())), B(Identity)(f))(xs).getIdentity(); 80 | 81 | console.log( 82 | B(foldMapDefault(Array, Array.of))(fmapDefault(Maybe, x => x * 10))( 83 | Compose([Just(10), Just(20), Nothing()]) 84 | ) 85 | ); 86 | 87 | // class Tuple { 88 | // constructor(a, b) { 89 | // this.a = a; 90 | // this.b = b; 91 | // } 92 | // 93 | // get fst() { 94 | // return this.a; 95 | // } 96 | // 97 | // get snd() { 98 | // return this.b 99 | // } 100 | // 101 | // get() { 102 | // return [this.a, this.b]; 103 | // } 104 | // } 105 | // 106 | // const [a, b] = new Tuple(10, 20).get(); 107 | // 108 | // console.log(a, b); 109 | 110 | const _Tuple = Type({ 111 | Tuple: { _1: K(true), _2: K(true) } 112 | }); 113 | 114 | const { Tuple } = _Tuple; 115 | 116 | Tuple.of = (empty, x) => Tuple(empty(), x); 117 | Tuple.empty = (empty1, empty2) => Tuple(empty1(), empty2()); 118 | 119 | Object.assign(_Tuple.prototype, { 120 | map(f) { 121 | return Tuple(this.fst(), f(this.snd())); 122 | }, 123 | ap(m) { 124 | const [u, f] = this; 125 | const [v, x] = m; 126 | 127 | return Tuple(u.concat(v), f(x)); 128 | }, 129 | chain(f) { 130 | const [u, a] = this; 131 | const [v, b] = f(a); 132 | 133 | return Tuple(u.concat(v), b); 134 | }, 135 | concat(m) { 136 | const [a1, b1] = this; 137 | const [a2, b2] = m; 138 | 139 | return Tuple(a1.concat(a2), b1.concat(b2)); 140 | }, 141 | fst() { 142 | return this.case({ 143 | Tuple(x, y) { 144 | return x; 145 | } 146 | }); 147 | }, 148 | snd() { 149 | return this.case({ 150 | Tuple(x, y) { 151 | return y; 152 | } 153 | }); 154 | }, 155 | foldMap(empty, f) { 156 | return f(this.snd()); 157 | }, 158 | foldr(f, z) { 159 | return f(this.snd(), y); 160 | }, 161 | traverse(of, f) { 162 | return f(this.snd()).map(_ => Tuple(this.fst(), _)); 163 | }, 164 | valueOf() { 165 | return [this.fst(), this.snd()]; 166 | } 167 | }); 168 | 169 | // const [x, y] = Tuple("10", 20).chain(x => Tuple(" Hello", x * 20)); 170 | const [x, y] = Tuple([10, 20], [30, 40]).traverse(Array.of, I); 171 | console.log([x, y]); 172 | 173 | // const _Writer = Type({ 174 | // Writer: {runWriter: Function} 175 | // }); 176 | // 177 | // const {Writer} = _Writer; 178 | // 179 | // const writer = tuple => Writer(K(tuple)); 180 | // 181 | // Writer.of = (x, empty) => writer(Tuple(x, empty())); 182 | // 183 | // 184 | // Object.assign(_Writer.prototype, { 185 | // map(f) { 186 | // const [a, w] = this.runWriter(); 187 | // 188 | // return writer(Tuple(f(a), w)); 189 | // }, 190 | // chain(f) { 191 | // const [a, w] = this.runWriter(); 192 | // const [a_, w_] = f(a).runWriter(); 193 | // 194 | // return writer(Tuple(a_, w.concat(w_))); 195 | // } 196 | // }); 197 | // 198 | // console.log([...Writer.of(10, () => "").chain(x => writer(Tuple(x + 20, `+${20}`))).runWriter()]) 199 | 200 | const isTuple = obj => { 201 | return obj.case({ 202 | Tuple: K(true) 203 | }); 204 | }; 205 | 206 | const _Writer = Type({ 207 | Writer: [isTuple] 208 | }); 209 | 210 | const { Writer } = _Writer; 211 | 212 | Writer.of = (x, empty) => Writer(Tuple(x, empty())); 213 | 214 | Object.assign(_Writer.prototype, { 215 | runWriter() { 216 | return this.case({ Writer: I }); 217 | }, 218 | execWriter() { 219 | return this.runWriter().snd(); 220 | }, 221 | evalWriter() { 222 | return this.runWriter().fst(); 223 | }, 224 | map(f) { 225 | const [a, w] = this.runWriter(); 226 | return Writer(Tuple(f(a), w)); 227 | }, 228 | chain(f) { 229 | const [a, w] = this.runWriter(); 230 | const [a_, w_] = f(a).runWriter(); 231 | 232 | return Writer(Tuple(a_, w.concat(w_))); 233 | } 234 | }); 235 | 236 | console.log(Writer(Tuple(10, 20)).runWriter()._1); 237 | 238 | console.log([Just(10), Nothing()].chain(m => m.maybe([], Array.of))); 239 | // console.log(Writer.of(10, () => "") 240 | // .chain(x => Writer(Tuple(x + 20, `${x} + ${20} = ${x+20} => `))) 241 | // .chain(x => Writer(Tuple(x + 30, `${x} + ${30} = ${x+30}`))) 242 | // .execWriter()) 243 | // const [x, y] = Tuple(10, 20).map(x => x * 10); 244 | // console.log([x, y]) 245 | // foldMap(Array,) 246 | -------------------------------------------------------------------------------- /tree.js: -------------------------------------------------------------------------------- 1 | const T = require('union-type'); 2 | const Rx = require('rx'); 3 | 4 | const { K, I } = require('./combinators'); 5 | 6 | const { Sum } = require('./sum'); 7 | 8 | var Tree = T({ 9 | Empty: [], 10 | Leaf: [K(true)], 11 | Node: [Tree, K(true), Tree] 12 | }); 13 | 14 | const { Empty, Leaf, Node } = Tree; 15 | 16 | Object.assign(Tree.prototype, { 17 | toString() { 18 | return this.case({ 19 | Empty: () => `Empty`, 20 | Leaf: v => `(Leaf ${v})`, 21 | Node: (l, x, r) => `Node ${l.toString()} ${x.toString()} ${r.toString()}` 22 | }); 23 | }, 24 | // Functor 25 | map(f) { 26 | return this.case({ 27 | Empty, 28 | Leaf: v => Leaf(f(v)), 29 | Node: (l, x, r) => Node(l.map(f), f(x), r.map(f)) 30 | }); 31 | }, 32 | // Foldable 33 | foldl(f, z) { 34 | return this.case({ 35 | Empty: K(z), 36 | Leaf: a => f(z)(a), 37 | Node: (l, x, r) => r.foldl(f, f(l.foldl(f, z))(x)) 38 | }); 39 | }, 40 | // Foldable 41 | foldr(f, z) { 42 | return this.case({ 43 | Empty: K(z), 44 | Leaf: a => f(a)(z), 45 | Node: (l, x, r) => l.foldr(f, f(x)(r.foldr(f, z))) 46 | }); 47 | }, 48 | // Foldable 49 | foldMap(empty, f) { 50 | return this.case({ 51 | Empty: empty, 52 | Leaf: a => f(a), 53 | Node: (l, x, r) => 54 | l.foldMap(empty, f).concat(f(x)).concat(r.foldMap(empty, f)) 55 | }); 56 | }, 57 | // Traversable 58 | traverse(of, f) { 59 | return this.case({ 60 | Empty: () => of(Empty()), 61 | Leaf: a => f(a).map(x => Leaf(x)), 62 | Node: (l, x, r) => 63 | l 64 | .traverse(of, f) 65 | .map(l => x => r => Node(l, x, r)) 66 | .ap(f(x)) 67 | .ap(r.traverse(of, f)) 68 | }); 69 | } 70 | }); 71 | 72 | const cons = x => xs => [x, ...xs]; 73 | const { Observable } = Rx; 74 | 75 | Observable.prototype.ap = function(o) { 76 | return this.combineLatest(o, (f, v) => f(v)); 77 | }; 78 | 79 | const _ZipObservable = T({ 80 | ZipObservable: [K(true)] 81 | }); 82 | 83 | const { ZipObservable } = _ZipObservable; 84 | 85 | ZipObservable.of = value => { 86 | return ZipObservable(Observable.interval(0).map(_ => value)); 87 | }; 88 | 89 | Object.assign(_ZipObservable.prototype, { 90 | map(f) { 91 | return ZipObservable.of(f).ap(this); 92 | }, 93 | ap(other) { 94 | return ZipObservable( 95 | Observable.zip(this.getZipObservable(), other.getZipObservable(), ( 96 | f, 97 | v 98 | ) => 99 | f(v)) 100 | ); 101 | }, 102 | getZipObservable() { 103 | return this.case({ 104 | ZipObservable(xs) { 105 | return xs; 106 | } 107 | }); 108 | } 109 | }); 110 | 111 | // const zipWith3 = f => xs => ys => zs => ZipObservable.of(f).ap(ZipObservable(xs)).ap(ZipObservable(ys)).ap(ZipObservable(zs)).getZipObservable(); 112 | // 113 | // zipWith3(x => y => z => [x, y ,z]) 114 | // (Observable.of(1,2,3)) 115 | // (Observable.of(4,5,6)) 116 | // (Observable.of(7,8)).subscribe(x => console.log(x)) 117 | 118 | require('./array'); 119 | const { ZipList } = require('./zip-list.js'); 120 | 121 | var _Max = T({ 122 | Max: [Number] 123 | }); 124 | 125 | const { Max } = _Max; 126 | 127 | Max.empty = () => Max(-Infinity); 128 | 129 | Object.assign(_Max.prototype, { 130 | getMax() { 131 | return this.case({ 132 | Max: I 133 | }); 134 | }, 135 | concat(m) { 136 | return Max(Math.max(this.getMax(), m.getMax())); 137 | } 138 | }); 139 | 140 | // data Stream = Stream { runStream :: Function } 141 | 142 | const _Stream = T({ 143 | Stream: { runStream: Function } 144 | }); 145 | 146 | const { Stream } = _Stream; 147 | 148 | Object.assign(_Stream.prototype, { 149 | map(f) { 150 | return Stream(o => this.runStream(x => o(f(x)))); 151 | } 152 | // runStream(fn) { 153 | // return this.case({ 154 | // Stream(subscribe) { 155 | // return subscribe(fn) 156 | // } 157 | // }) 158 | // },,,,,,,,,, 159 | }); 160 | 161 | Stream(fn => { 162 | fn(10); 163 | }) 164 | .map(x => x * 10) 165 | .runStream(x => console.log(x)); 166 | // Node( 167 | // Leaf([1]), 168 | // [10,20,30,40], 169 | // Leaf([100,200,300]) 170 | // ).traverse(Array.of, x => x) 171 | // .map(x => x.foldMap(Max.empty, Max)) 172 | // .forEach(x => console.log(x.getMax())) 173 | // Node( 174 | // Leaf(Observable.interval(2500).scan((a, b) => a + b, 0)), 175 | // Observable.interval(500).scan((a, b) => a + b, 0), 176 | // Leaf(Observable.interval(1000).scan((a, b) => a + b, 0)) 177 | // ).traverse(ZipObservable.of, o => ZipObservable(o)) // Tree (Observable a) => Observable (Tree a) 178 | // // .map(x => x.foldMap(Sum.empty, Sum).getSum()) // Tree => Number 179 | // // .scan((a, b) => a + b.foldMap(Sum.empty, Sum).getSum(), 0) 180 | // .getZipObservable() 181 | // .subscribe(x => console.log(x.toString())) 182 | // Node( 183 | // Leaf(Observable.of(10)), 184 | // Observable.of(20), 185 | // Leaf(Observable.of(30)) 186 | // ).foldr(n => a => n.concat(a), Observable.empty()).subscribe(x => console.log(x)) 187 | -------------------------------------------------------------------------------- /tree2.js: -------------------------------------------------------------------------------- 1 | const T = require('union-type'); 2 | const R = require('ramda'); 3 | 4 | const { Observable } = require('rx/dist/rx.all'); 5 | 6 | require('./array'); 7 | 8 | const { K } = require('./combinators'); 9 | 10 | const { Sum } = require('./sum'); 11 | const { foldMap, map, liftA2 } = require('.'); 12 | 13 | // const {ZipList} = require('./zip-list'); 14 | 15 | Object.assign(Observable.prototype, { 16 | // Applicative 17 | ap(m) { 18 | return this.combineLatest(m, (f, v) => f(v)); 19 | } 20 | }); 21 | 22 | Array.prototype.toString = function() { 23 | return `[${this.join(',')}]`; 24 | }; 25 | 26 | // data Tree a = Node { root :: a, children :: [Tree a] } 27 | var Tree = T({ 28 | Node: { 29 | root: R.T, 30 | children: xs => xs.every(x => Tree.prototype.isPrototypeOf(x)) 31 | // subForest: x => x.length === 0 || T.ListOf(Tree)(x) // check it for Forest = [Tree], 32 | } 33 | }); 34 | 35 | const { Node } = Tree; 36 | 37 | // Pointed Functor 38 | Tree.of = x => Node(x, []); 39 | 40 | Object.assign(Tree.prototype, { 41 | toString() { 42 | return this.case({ 43 | Node(x, xs) { 44 | return `Node(${x.toString()}, ${xs.toString()})`; 45 | } 46 | }); 47 | }, 48 | // Monoid? 49 | concat(t) { 50 | return this.case({ 51 | Node(x, xs) { 52 | return t.case({ 53 | Node(x1, xs1) { 54 | return Node(x.concat(x1), R.zipWith(R.concat, xs, xs1)); 55 | } 56 | }); 57 | } 58 | }); 59 | }, 60 | // Setoid? 61 | equals(t) { 62 | return this.case({ 63 | Node(x, xs) { 64 | return t.case({ 65 | Node(x1, xs1) { 66 | return x === x1 && 67 | xs.length === xs1.length && 68 | R.all(Boolean)(R.zipWith((x, y) => x.equals(y), xs, xs1)); 69 | } 70 | }); 71 | } 72 | }); 73 | }, 74 | // Functor 75 | map(f) { 76 | return this.case({ 77 | Node(x, xs) { 78 | return Node(f(x), xs.map(map(f))); 79 | } 80 | }); 81 | }, 82 | // Applicative 83 | ap(m) { 84 | return this.case({ 85 | Node(f, fs) { 86 | return m.case({ 87 | Node(x, xs) { 88 | return Node(x, xs.map(map(f)).concat(fs.map(s => s.ap(m)))); 89 | } 90 | }); 91 | // return Node( 92 | // f(m.rootLabel), 93 | // m.subForest.map(map(f)).concat(fs.map(s => s.ap(m))) 94 | // ) 95 | } 96 | }); 97 | }, 98 | // Traversable 99 | traverse(of, f) { 100 | return this.case({ 101 | Node(x, xs) { 102 | return f(x) 103 | .map(r => s => Node(r, s)) 104 | .ap(xs.traverse(of, t => t.traverse(of, f))); 105 | } 106 | }); 107 | }, 108 | // foldMap(empty, f) { 109 | // return this.case({ 110 | // Node(x, xs) { 111 | // return f(x).concat(foldMap(empty, foldMap(empty, f))(xs)) 112 | // } 113 | // }) 114 | // }, 115 | // Foldable 116 | foldl(f, z) { 117 | return this.case({ 118 | Node(x, xs) { 119 | // f(l.foldl(f, z))(x) 120 | return xs.foldl((a, b) => b.foldl(f, a), f(z, x)); 121 | // return f(xs.foldl((a, b) => b.foldl(f, a), z), x) 122 | } 123 | }); 124 | }, 125 | // Foldable 126 | foldr(f, z) { 127 | return this.case({ 128 | // l.foldr(f, f(x)(r.foldr(f, z))) 129 | Node(x, xs) { 130 | // return xs.foldr((a, b) => a.foldr(f, b), f(x, z)) 131 | return f(x, xs.foldr((a, b) => a.foldr(f, b), z)); 132 | } 133 | // Node: (x, xs) => xs.foldr((a, b) => a.foldr(f, b), f(x, z)), 134 | }); 135 | }, 136 | // Monad 137 | chain(f) { 138 | return this.case({ 139 | Node(x, xs) { 140 | // const [_x, _xs] = f(x) 141 | const { root, children } = f(x); 142 | 143 | // return Node(_x, _xs.concat(xs.map(v => v.chain(f)))) 144 | return Node(root, children.concat(xs.map(v => v.chain(f)))); 145 | } 146 | }); 147 | }, 148 | // Iterator 149 | *[Symbol.iterator]() { 150 | yield this.root; 151 | for (let c of this.children) { 152 | yield* c; 153 | } 154 | } 155 | }); 156 | 157 | console.log( 158 | Node(10, []).map(x => x * 10), 159 | Node(10, [Node(20, []), Node(30, [])]).equals( 160 | Node(10, [Node(20, []), Node(30, [])]) 161 | ), 162 | Node([10], [Node([20], [Node([30], [])])]) 163 | .concat(Node([100], [Node([200], [Node([300], [])]), Node([400], [])])) 164 | .toString() 165 | ); 166 | 167 | const depthFirst = t => t.case({ 168 | Node(x, xs) { 169 | return [].concat(x, ...xs.map(_ => depthFirst(_))); 170 | } 171 | }); 172 | 173 | console.log(depthFirst(Node(10, [Node(20, []), Node(30, [])]))); 174 | 175 | console.log( 176 | R.sequence(R.of, R.repeat([-1, 0, 1], 2)) 177 | // foldMap(Sum.empty, Sum) 178 | // [...Node(10, []) 179 | // .chain(x => Node(x, [Node(x * x, []), Node(x * x * x, [])]))] 180 | // .getSum() 181 | // .foldr((a, b) => [a].concat(b), []) 182 | // .foldl((a, b) => a.concat([b]), []) 183 | // foldMap(Sum.empty, Sum)(Node(10, [ 184 | // Node(20, []), 185 | // Node(30, []) 186 | // ])).getSum() 187 | //.foldl((a, b) => a - b, 0) 188 | // .foldMap(Sum.empty, Sum).getSum(), 189 | ); 190 | // Node(Observable.interval(500), [ 191 | // Node(Observable.interval(1000), []), 192 | // Node(Observable.interval(1500), []) 193 | // ]).traverse(Observable.of, x => x).subscribe(x => console.log(x.toString())) 194 | // console.log( 195 | // Node(Observable.of([10, 20]), [ 196 | // Node(Observable.of([30, 40]), []), 197 | // Node(Observable.of([50, 60]), []) 198 | // ]).traverse(Observable.of, x => x) 199 | // ) 200 | // console.log( 201 | // Node(x => x * 10, [Node(x => x * 20, []), Node(x => x * 30, [])]) 202 | // .ap(Node(10, [Node(20, []), Node(30, [])])) 203 | // .map(x => x * 10) 204 | // ) 205 | -------------------------------------------------------------------------------- /tree3.js: -------------------------------------------------------------------------------- 1 | const T = require('union-type'); 2 | const { K } = require('./combinators'); 3 | 4 | var Tree = T({ 5 | Empty: [], 6 | Node: [K(true), Tree, Tree] 7 | }); 8 | 9 | const { Empty, Node } = Tree; 10 | 11 | Object.assign(Tree.prototype, { 12 | drawTree() { 13 | const draw = t => t.case({ 14 | Empty: K(['--(null)']), 15 | Node(v, l, r) { 16 | const [r_, ...rs] = draw(r); 17 | const ls = draw(l); 18 | 19 | const v_ = `--${v}`; 20 | const ls_ = ls.map(x => ` |${x}`); 21 | const rs_ = [' `' + r_, ...rs.map(x => ` ${x}`)]; 22 | 23 | return [v_, ...ls_, ...rs_]; 24 | } 25 | }); 26 | 27 | return draw(this).join('\n'); 28 | } 29 | }); 30 | 31 | const insert = (t, x) => { 32 | return t.case({ 33 | Empty() { 34 | return Node(x, Empty(), Empty()); 35 | }, 36 | Node(y, l, r) { 37 | if (x < y) return Node(y, insert(l, x), r); 38 | else if (y < x) return Node(y, l, insert(r, x)); 39 | else return t; 40 | } 41 | }); 42 | }; 43 | 44 | const buildTree_ = t => { 45 | if (t.length === 0) { 46 | return Empty(); 47 | } else { 48 | const [x, ...xs] = t; 49 | return insert(buildTree_(xs), x); 50 | } 51 | }; 52 | 53 | const buildTreeFromSortedList = l => { 54 | if (l.length === 0) return Empty(); 55 | 56 | const n = Math.floor(l.length / 2); 57 | const lt = l.slice(0, n); 58 | const [x, ...rt] = l.slice(-(n + 1)); 59 | 60 | return Node(x, buildTreeFromSortedList(lt), buildTreeFromSortedList(rt)); 61 | }; 62 | 63 | console.log( 64 | buildTreeFromSortedList([1, 2, 3, 4, 5, 6]).drawTree() 65 | // [1,2,3,4,5,6].reduceRight(insert, Empty()).drawTree() 66 | ); 67 | -------------------------------------------------------------------------------- /zip-list.js: -------------------------------------------------------------------------------- 1 | require('./array'); 2 | const Type = require('union-type'); 3 | const { liftA2, map, traverse, foldMap } = require('.'); 4 | const { B, I } = require('./combinators'); 5 | 6 | const isIterable = obj => 7 | obj != null && typeof obj[Symbol.iterator] === 'function'; 8 | 9 | const _ZipList = Type({ 10 | ZipList: [isIterable] 11 | }); 12 | 13 | const { ZipList } = _ZipList; 14 | 15 | ZipList.of = value => { 16 | const repeater = function*(value) { 17 | for (;;) { 18 | yield value; 19 | } 20 | }; 21 | 22 | return ZipList(repeater(value)); 23 | }; 24 | 25 | Object.assign(_ZipList.prototype, { 26 | map(f) { 27 | return ZipList.of(f).ap(this); 28 | }, 29 | ap(other) { 30 | const _ap = function*(xs, ys) { 31 | const iterators = [xs, ys].map(_ => _[Symbol.iterator]()); 32 | 33 | let [x, y] = iterators.map(_ => _.next()); 34 | while (!x.done && !y.done) { 35 | yield x.value(y.value); 36 | [x, y] = iterators.map(_ => _.next()); 37 | } 38 | }; 39 | 40 | return ZipList(_ap(this, other)); 41 | }, 42 | *[Symbol.iterator]() { 43 | yield* this.case({ 44 | ZipList(xs) { 45 | return xs; 46 | } 47 | }); 48 | }, 49 | foldMap(empty, f) { 50 | return foldMap(empty, f)(this.getZipList()); 51 | }, 52 | traverse(of, f) { 53 | return this.getZipList().traverse(of, f).map(ZipList); 54 | }, 55 | getZipList() { 56 | return [...this]; 57 | } 58 | }); 59 | 60 | const zipWith = f => 61 | xs => ys => ZipList.of(f).ap(ZipList(xs)).ap(ZipList(ys)).getZipList(); 62 | const zipWith3 = f => 63 | xs => 64 | ys => 65 | zs => 66 | ZipList 67 | .of(f) 68 | .ap(ZipList(xs)) 69 | .ap(ZipList(ys)) 70 | .ap(ZipList(zs)) 71 | .getZipList(); 72 | 73 | zipWith3(x => y => z => [x, y, z])([1, 2, 3])([4, 5, 6])([7, 8]); //> 1,4,7,2,5,8 74 | console.log(zipWith3(x => y => z => [x, y, z])([1, 2, 3])([4, 5, 6])([7, 8])); 75 | 76 | console.log(zipWith(x => y => [x, y])([1, 2, 3])([4, 5, 6])); 77 | 78 | console.log( 79 | liftA2(x => y => x * y)(ZipList.of(10))(ZipList([1, 2, 3])).getZipList() 80 | ); 81 | 82 | const transpose = xss => traverse(ZipList.of, ZipList)(xss).getZipList(); 83 | 84 | transpose([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]); //> 1,5,9,2,6,10,3,7,11,4,8,12 85 | foldMap(Array.empty, Array.of)(ZipList([1, 2, 3])); //> 1,2,3 86 | console.log(transpose([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])); 87 | 88 | console.log(foldMap(Array.empty, Array.of)(ZipList([1, 2, 3]))); 89 | 90 | module.exports = { 91 | ZipList 92 | }; 93 | --------------------------------------------------------------------------------