diff --git a/package.json b/package.json index 029cd79..12c4c1b 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "test": "mocha" }, "dependencies": { - "ramda": ">=0.15.0" + "ramda": ">=0.15.0", + "sanctuary-type-classes": "0.2.0" }, "devDependencies": { "jscs": "1.13.x", diff --git a/src/Either.js b/src/Either.js index 8d6c1bb..9e345ac 100644 --- a/src/Either.js +++ b/src/Either.js @@ -125,5 +125,10 @@ Either.Left = function(value) { return new _Left(value); }; +require('./internal/fl-patch')([ + Either, Either.prototype, + _Left, _Left.prototype, + _Right, _Right.prototype, +]); module.exports = Either; diff --git a/src/Future.js b/src/Future.js index 4d8dd7e..2d02f38 100644 --- a/src/Future.js +++ b/src/Future.js @@ -203,4 +203,6 @@ Future.cache = function(f) { }); }; +require('./internal/fl-patch')([Future, Future.prototype]); + module.exports = Future; diff --git a/src/IO.js b/src/IO.js index 28b87cb..cfa4050 100644 --- a/src/IO.js +++ b/src/IO.js @@ -1,5 +1,6 @@ var compose = require('ramda/src/compose'); var toString = require('ramda/src/toString'); +var Z = require('sanctuary-type-classes'); var util = require('./internal/util'); @@ -42,9 +43,9 @@ IO.prototype.map = function(f) { // `this` IO must wrap a function `f` that takes an IO (`thatIo`) as input // `f` must return an IO IO.prototype.ap = function(thatIo) { - return this.chain(function(f) { - return thatIo.map(f); - }); + return Z.chain(function(f) { + return Z.map(f, thatIo); + }, this); }; IO.runIO = function(io) { @@ -64,3 +65,5 @@ IO.of = IO.prototype.of; IO.prototype.toString = function() { return 'IO(' + toString(this.fn) + ')'; }; + +require('./internal/fl-patch')([IO, IO.prototype]); diff --git a/src/Identity.js b/src/Identity.js index 4744323..f02911c 100644 --- a/src/Identity.js +++ b/src/Identity.js @@ -1,5 +1,5 @@ var toString = require('ramda/src/toString'); - +var Z = require('sanctuary-type-classes'); var util = require('./internal/util'); @@ -52,7 +52,7 @@ Identity.prototype.map = function(f) { * @sig (Identity[a -> b], f: Applicative[_]) => f[a] -> f[b] */ Identity.prototype.ap = function(app) { - return app.map(this.value); + return Z.map(this.value, app); }; /** @@ -94,4 +94,6 @@ Identity.prototype.toString = function() { return 'Identity(' + toString(this.value) + ')'; }; +require('./internal/fl-patch')([Identity, Identity.prototype]); + module.exports = Identity; diff --git a/src/Maybe.js b/src/Maybe.js index e3fc228..c224d4e 100644 --- a/src/Maybe.js +++ b/src/Maybe.js @@ -1,5 +1,6 @@ var toString = require('ramda/src/toString'); var curry = require('ramda/src/curry'); +var Z = require('sanctuary-type-classes'); var util = require('./internal/util.js'); @@ -46,15 +47,15 @@ Maybe.isNothing = function(x) { }; Maybe.maybe = curry(function(nothingVal, justFn, m) { - return m.reduce(function(_, x) { + return Z.reduce(function(_, x) { return justFn(x); - }, nothingVal); + }, nothingVal, m); }); // semigroup Just.prototype.concat = function(that) { return that.isNothing ? this : this.of( - this.value.concat(that.value) + Z.concat(this.value, that.value) ); }; @@ -62,7 +63,7 @@ Nothing.prototype.concat = util.identity; // functor Just.prototype.map = function(f) { - return this.of(f(this.value)); + return Z.of(this, f(this.value)); }; Nothing.prototype.map = util.returnThis; @@ -71,7 +72,7 @@ Nothing.prototype.map = util.returnThis; // takes a Maybe that wraps a function (`app`) and applies its `map` // method to this Maybe's value, which must be a function. Just.prototype.ap = function(m) { - return m.map(this.value); + return Z.map(this.value, m); }; Nothing.prototype.ap = util.returnThis; @@ -151,4 +152,10 @@ Nothing.prototype.toString = function() { return 'Maybe.Nothing()'; }; +require('./internal/fl-patch')([ + Maybe, Maybe.prototype, + Nothing, Nothing.prototype, + Just, Just.prototype, +]); + module.exports = Maybe; diff --git a/src/Reader.js b/src/Reader.js index 9e723d1..9acb69d 100644 --- a/src/Reader.js +++ b/src/Reader.js @@ -2,7 +2,9 @@ var compose = require('ramda/src/compose'); var identity = require('ramda/src/identity'); var toString = require('ramda/src/toString'); var always = require('ramda/src/always'); +var Z = require('sanctuary-type-classes'); +var patchAll = require('./internal/fl-patch'); function Reader(run) { if (!(this instanceof Reader)) { @@ -25,15 +27,15 @@ Reader.prototype.chain = function(f) { }; Reader.prototype.ap = function(a) { - return this.chain(function(f) { - return a.map(f); - }); + return Z.chain(function(f) { + return Z.map(f, a); + }, this); }; Reader.prototype.map = function(f) { - return this.chain(function(a) { - return Reader.of(f(a)); - }); + return Z.chain(function(a) { + return Z.of(Reader, f(a)); + }, this); }; Reader.prototype.of = function(a) { @@ -63,7 +65,7 @@ Reader.T = function(M) { ReaderT.prototype.of = ReaderT.of = function(a) { return ReaderT(function() { - return M.of(a); + return Z.of(M, a); }); }; @@ -71,22 +73,22 @@ Reader.T = function(M) { var readerT = this; return ReaderT(function(e) { var m = readerT.run(e); - return m.chain(function(a) { + return Z.chain(function(a) { return f(a).run(e); - }); + }, m); }); }; ReaderT.prototype.map = function(f) { - return this.chain(function(a) { - return ReaderT.of(f(a)); - }); + return Z.chain(function(a) { + return Z.of(ReaderT, f(a)); + }, this); }; ReaderT.prototype.ap = function(a) { var readerT = this; return ReaderT(function(e) { - return readerT.run(e).ap(a.run(e)); + return Z.ap(readerT.run(e), a.run(e)); }); }; @@ -94,7 +96,11 @@ Reader.T = function(M) { return 'ReaderT[' + M.name + '](' + toString(this.run) + ')'; }; + patchAll([ReaderT, ReaderT.prototype]); + return ReaderT; }; +patchAll([Reader, Reader.prototype]); + module.exports = Reader; diff --git a/src/State.js b/src/State.js index 2822a31..af57c8d 100644 --- a/src/State.js +++ b/src/State.js @@ -1,9 +1,10 @@ var curry = require('ramda/src/curry'); +var Z = require('sanctuary-type-classes'); var Identity = require('./Identity'); var Tuple = require('./Tuple'); var util = require('./internal/util'); - +var patchAll = require('./internal/fl-patch'); function T(M) { function StateT(run) { @@ -24,14 +25,14 @@ function T(M) { StateT.prototype.chain = function(f) { var state = this; return StateT(function(s) { - return state._run(s).chain(function(t) { + return Z.chain(function(t) { return f(Tuple.fst(t))._run(Tuple.snd(t)); - }); + }, state._run(s)); }); }; StateT.of = StateT.prototype.of = function(a) { return StateT(function (s) { - return M.of(Tuple(a, s)); + return Z.of(M,Tuple(a, s)); }); }; StateT.prototype.ap = util.deriveAp(StateT); @@ -39,41 +40,44 @@ function T(M) { StateT.tailRec = curry(function(stepFn, init) { return StateT(function(s) { return M.tailRec(function(t) { - return stepFn(Tuple.fst(t))._run(Tuple.snd(t)).chain(function (t_) { - return M.of(Tuple.fst(t_).bimap( + return Z.chain(function (t_) { + return Z.of(M, Z.bimap( function(a) { return Tuple(a, Tuple.snd(t_)); }, - function(b) { return Tuple(b, Tuple.snd(t_)); } + function(b) { return Tuple(b, Tuple.snd(t_)); }, + Tuple.fst(t_) )); - }); + }, stepFn(Tuple.fst(t))._run(Tuple.snd(t))); }, Tuple(init, s)); }); }); StateT.lift = function(ma) { return StateT(function(s) { - return ma.chain(function(a) { - return M.of(Tuple(a, s)); + return Z.chain(ma, function(a) { + return Z.of(M, Tuple(a, s)); }); }); }; StateT.get = StateT(function(s) { - return M.of(Tuple(s, s)); + return Z.of(M, Tuple(s, s)); }); StateT.gets = function(f) { return StateT(function(s) { - return M.of(Tuple(f(s), s)); + return Z.of(M, Tuple(f(s), s)); }); }; StateT.put = function(s) { return StateT(function(_) { - return M.of(Tuple(void _, s)); + return Z.of(M, Tuple(void _, s)); }); }; StateT.modify = function(f) { return StateT(function(s) { - return M.of(Tuple(void 0, f(s))); + return Z.of(M, Tuple(void 0, f(s))); }); }; + patchAll([StateT, StateT.prototype]); + return StateT; } @@ -83,4 +87,6 @@ State.prototype.run = function(s) { return this._run(s).value; }; +patchAll([State, State.prototype]); + module.exports = State; diff --git a/src/Tuple.js b/src/Tuple.js index 719afc1..4f48b98 100644 --- a/src/Tuple.js +++ b/src/Tuple.js @@ -1,5 +1,6 @@ var toString = require('ramda/src/toString'); var equals = require('ramda/src/equals'); +var Z = require('sanctuary-type-classes'); function Tuple(x, y) { @@ -21,14 +22,6 @@ function _Tuple(x, y) { this.length = 2; } -function ensureConcat(xs) { - xs.forEach(function(x) { - if (typeof x.concat != 'function') { - throw new TypeError(toString(x) + ' must be a semigroup to perform this operation'); - } - }); -} - Tuple.fst = function(x) { return x[0]; }; @@ -41,8 +34,7 @@ _Tuple.prototype['@@type'] = 'ramda-fantasy/Tuple'; // semigroup _Tuple.prototype.concat = function(x) { - ensureConcat([this[0], this[1]]); - return Tuple(this[0].concat(x[0]), this[1].concat(x[1])); + return Tuple(Z.concat(this[0], x[0]), Z.concat(this[1], x[1])); }; // functor @@ -52,8 +44,7 @@ _Tuple.prototype.map = function(f) { // apply _Tuple.prototype.ap = function(m) { - ensureConcat([this[0]]); - return Tuple(this[0].concat(m[0]), this[1](m[1])); + return Tuple(Z.concat(this[0], m[0]), this[1](m[1])); }; // setoid @@ -65,4 +56,7 @@ _Tuple.prototype.toString = function() { return 'Tuple(' + toString(this[0]) + ', ' + toString(this[1]) + ')'; }; +require('./internal/fl-patch')([_Tuple, _Tuple.prototype]); + + module.exports = Tuple; diff --git a/src/internal/fl-patch.js b/src/internal/fl-patch.js new file mode 100644 index 0000000..aa7d76a --- /dev/null +++ b/src/internal/fl-patch.js @@ -0,0 +1,22 @@ +var fl = require('./fl.js'); +var fixedAp = function(f) { + return f.ap(this); +}; + +var patch = function(obj){ + return Object.keys(fl).forEach(function(key) { + if (typeof obj[key] === 'function') { + if (key === 'ap') { + obj[fl[key]] = fixedAp; + } else { + obj[fl[key]] = obj[key]; + } + } + }); +}; + +var patchAll = function(objs){ + return objs.forEach(patch); +}; + +module.exports = patchAll; diff --git a/src/internal/fl.js b/src/internal/fl.js new file mode 100644 index 0000000..1b5cb48 --- /dev/null +++ b/src/internal/fl.js @@ -0,0 +1,16 @@ +module.exports = { + equals: 'fantasy-land/equals', + concat: 'fantasy-land/concat', + empty: 'fantasy-land/empty', + map: 'fantasy-land/map', + ap: 'fantasy-land/ap', + of: 'fantasy-land/of', + reduce: 'fantasy-land/reduce', + traverse: 'fantasy-land/traverse', + chain: 'fantasy-land/chain', + chainRec: 'fantasy-land/chainRec', + extend: 'fantasy-land/extend', + extract: 'fantasy-land/extract', + bimap: 'fantasy-land/bimap', + promap: 'fantasy-land/promap', +}; diff --git a/src/internal/util.js b/src/internal/util.js index d520e43..f7eef57 100644 --- a/src/internal/util.js +++ b/src/internal/util.js @@ -1,5 +1,5 @@ var _equals = require('ramda/src/equals'); - +var Z = require('sanctuary-type-classes'); module.exports = { @@ -48,19 +48,19 @@ module.exports = { deriveAp: function (Type) { return function(fa) { - return this.chain(function (f) { - return fa.chain(function (a) { - return Type.of(f(a)); - }); - }); + return Z.chain(function (f) { + return Z.chain(function (a) { + return Z.of(Type, f(a)); + }, fa); + }, this); }; }, deriveMap: function (Type) { return function (f) { - return this.chain(function (a) { - return Type.of(f(a)); - }); + return Z.chain(function (a) { + return Z.of(Type, f(a)); + }, this); }; }