ramda-fantasy
Advanced tools
Comparing version 0.6.1 to 0.7.0
{ | ||
"name": "ramda-fantasy", | ||
"description": "Fantasy Land compatible types for easy integration with Ramda", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"authors": [ | ||
@@ -6,0 +6,0 @@ { |
# Future | ||
The `Future` type is used to represent some future, often asynchronous, | ||
action that may potentially fail. It is similar to native the JS `Promise` type, | ||
action that may potentially fail. It is similar to the native JS `Promise` type, | ||
however the computation of a `Promise` is executed immediately, while the | ||
@@ -6,0 +6,0 @@ execution of a `Future` instance is delayed until explicitly requested. |
19
index.js
module.exports = { | ||
Either: require('./src/Either'), | ||
Future: require('./src/Future'), | ||
Identity: require('./src/Identity'), | ||
IO: require('./src/IO'), | ||
lift2: require('./src/lift2'), | ||
lift3: require('./src/lift3'), | ||
Maybe: require('./src/Maybe'), | ||
Tuple: require('./src/Tuple'), | ||
Reader: require('./src/Reader') | ||
Either: require('./src/Either'), | ||
Future: require('./src/Future'), | ||
Identity: require('./src/Identity'), | ||
IO: require('./src/IO'), | ||
lift2: require('./src/lift2'), | ||
lift3: require('./src/lift3'), | ||
Maybe: require('./src/Maybe'), | ||
Reader: require('./src/Reader'), | ||
State: require('./src/State'), | ||
Tuple: require('./src/Tuple') | ||
}; |
@@ -16,3 +16,3 @@ { | ||
"description": "Fantasy Land compatible types for easy integration with Ramda", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"homepage": "https://www.github.com/ramda/ramda-fantasy", | ||
@@ -41,2 +41,3 @@ "license": "MIT", | ||
"mocha": "^2.1.0", | ||
"promise": "7.1.1", | ||
"uglify-js": "2.4.x", | ||
@@ -43,0 +44,0 @@ "xyz": "0.5.x" |
@@ -13,11 +13,11 @@ ramda-fantasy | ||
| Name | [Setoid][3] | [Semigroup][4] | [Functor][5] | [Applicative][6] | [Monad][7] | [Foldable][8] | | ||
| --------------- | :----------: | :------------: | :----------: | :--------------: | :--------: | :-----------: | | ||
| [Either][9] | **✔︎** | | **✔︎** | **✔︎** | **✔︎** | | | ||
| [Future][10] | | | **✔︎** | **✔︎** | **✔︎** | | | ||
| [Identity][11] | **✔︎** | | **✔︎** | **✔︎** | **✔︎** | | | ||
| [IO][12] | | | **✔︎** | **✔︎** | **✔︎** | | | ||
| [Maybe][13] | **✔︎** | | **✔︎** | **✔︎** | **✔︎** | **✔︎** | | ||
| [Reader][14] | | | **✔︎** | **✔︎** | **✔︎** | | | ||
| [Tuple][15] | **✔︎** | **✔︎** | **✔︎** | | | | | ||
| Name | [Setoid][3] | [Semigroup][4] | [Functor][5] | [Applicative][6] | [Monad][7] | [Foldable][8] | [ChainRec][16] | | ||
| --------------- | :----------: | :------------: | :----------: | :--------------: | :--------: | :-----------: | :------------: | | ||
| [Either][9] | **✔︎** | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | | ||
| [Future][10] | | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | | ||
| [Identity][11] | **✔︎** | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | | ||
| [IO][12] | | | **✔︎** | **✔︎** | **✔︎** | | **✔︎** | | ||
| [Maybe][13] | **✔︎** | | **✔︎** | **✔︎** | **✔︎** | **✔︎** | **✔︎** | | ||
| [Reader][14] | | | **✔︎** | **✔︎** | **✔︎** | | | | ||
| [Tuple][15] | **✔︎** | **✔︎** | **✔︎** | | | | | | ||
@@ -45,1 +45,2 @@ | ||
[15]: docs/Tuple.md | ||
[16]: https://github.com/fantasyland/fantasy-land#chainrec |
@@ -1,2 +0,3 @@ | ||
var R = require('ramda'); | ||
var curry = require('ramda/src/curry'); | ||
var toString = require('ramda/src/toString'); | ||
@@ -32,3 +33,3 @@ var util = require('./internal/util'); | ||
Either.either = R.curry(function either(leftFn, rightFn, e) { | ||
Either.either = curry(function either(leftFn, rightFn, e) { | ||
if (e instanceof _Left) { | ||
@@ -73,2 +74,15 @@ return leftFn(e.value); | ||
//chainRec | ||
Either.chainRec = Either.prototype.chainRec = function(f, i) { | ||
var res, state = util.chainRecNext(i); | ||
while (state.isNext) { | ||
res = f(util.chainRecNext, util.chainRecDone, state.value); | ||
if (Either.isLeft(res)) { | ||
return res; | ||
} | ||
state = res.value; | ||
} | ||
return Either.Right(state.value); | ||
}; | ||
_Right.prototype.bimap = function(_, f) { | ||
@@ -83,3 +97,3 @@ return new _Right(f(this.value)); | ||
_Right.prototype.toString = function() { | ||
return 'Either.Right(' + R.toString(this.value) + ')'; | ||
return 'Either.Right(' + toString(this.value) + ')'; | ||
}; | ||
@@ -110,3 +124,3 @@ | ||
_Left.prototype.toString = function() { | ||
return 'Either.Left(' + R.toString(this.value) + ')'; | ||
return 'Either.Left(' + toString(this.value) + ')'; | ||
}; | ||
@@ -113,0 +127,0 @@ |
@@ -1,3 +0,8 @@ | ||
var R = require('ramda'); | ||
var once = require('ramda/src/once'); | ||
var forEach = require('ramda/src/forEach'); | ||
var toString = require('ramda/src/toString'); | ||
var curry = require('ramda/src/curry'); | ||
var util = require('./internal/util'); | ||
function jail(handler, f){ | ||
@@ -38,3 +43,3 @@ return function(a){ | ||
var applyFn, val; | ||
var doReject = R.once(rej); | ||
var doReject = once(rej); | ||
@@ -83,2 +88,47 @@ var resolveIfDone = jail(doReject, function() { | ||
// chainRec | ||
// | ||
// Heavily influenced by the Aff MonadRec instance | ||
// https://github.com/slamdata/purescript-aff/blob/51106474122d0e5aec8e3d5da5bb66cfe8062f55/src/Control/Monad/Aff.js#L263-L322 | ||
Future.chainRec = Future.prototype.chainRec = function(f, a) { | ||
return Future(function(reject, resolve) { | ||
return function go(acc) { | ||
// isSync could be in three possable states | ||
// * null - unresolved status | ||
// * true - synchronous future | ||
// * false - asynchronous future | ||
var isSync = null; | ||
var state = util.chainRecNext(acc); | ||
var onResolve = function(v) { | ||
// If the `isSync` is still unresolved, we have observed a | ||
// synchronous future. Otherwise, `isSync` will be `false`. | ||
if (isSync === null) { | ||
isSync = true; | ||
// Store the result for further synchronous processing. | ||
state = v; | ||
} else { | ||
// When we have observed an asynchronous future, we use normal | ||
// recursion. This is safe because we will be on a new stack. | ||
(v.isNext ? go : resolve)(v.value); | ||
} | ||
}; | ||
while (state.isNext) { | ||
isSync = null; | ||
f(util.chainRecNext, util.chainRecDone, state.value).fork(reject, onResolve); | ||
// If the `isSync` has already resolved to `true` by our `onResolve`, then | ||
// we have observed a synchronous future. Otherwise it will still be `null`. | ||
if (isSync === true) { | ||
continue; | ||
} else { | ||
// If the status has not resolved yet, then we have observed an | ||
// asynchronous or failed future so update status and exit the loop. | ||
isSync = false; | ||
return; | ||
} | ||
} | ||
resolve(state.value); | ||
}(a); | ||
}); | ||
}; | ||
// chainReject | ||
@@ -117,3 +167,3 @@ // Like chain but operates on the reject instead of the resolve case. | ||
Future.prototype.toString = function() { | ||
return 'Future(' + R.toString(this._fork) + ')'; | ||
return 'Future(' + toString(this._fork) + ')'; | ||
}; | ||
@@ -126,7 +176,7 @@ | ||
var handleCompletion = R.curry(function(newStatus, cb, val) { | ||
var handleCompletion = curry(function(newStatus, cb, val) { | ||
status = newStatus; | ||
cachedValue = val; | ||
cb(val); | ||
R.forEach(function(listener) { | ||
forEach(function(listener) { | ||
listener[status](cachedValue); | ||
@@ -133,0 +183,0 @@ }, listeners); |
@@ -1,2 +0,2 @@ | ||
var R = require('ramda'); | ||
var toString = require('ramda/src/toString'); | ||
@@ -71,2 +71,11 @@ var util = require('./internal/util'); | ||
// chainRec | ||
Identity.chainRec = Identity.prototype.chainRec = function(f, i) { | ||
var state = util.chainRecNext(i); | ||
while (state.isNext) { | ||
state = f(util.chainRecNext, util.chainRecDone, state.value).get(); | ||
} | ||
return Identity(state.value); | ||
}; | ||
/** | ||
@@ -86,5 +95,5 @@ * Returns the value of `Identity[a]` | ||
Identity.prototype.toString = function() { | ||
return 'Identity(' + R.toString(this.value) + ')'; | ||
return 'Identity(' + toString(this.value) + ')'; | ||
}; | ||
module.exports = Identity; |
@@ -1,2 +0,2 @@ | ||
var _equals = require('ramda').equals; | ||
var _equals = require('ramda/src/equals'); | ||
@@ -39,4 +39,30 @@ | ||
returnThis: function() { return this; } | ||
returnThis: function() { return this; }, | ||
chainRecNext: function(v) { | ||
return { isNext: true, value: v }; | ||
}, | ||
chainRecDone: function(v) { | ||
return { isNext: false, value: v }; | ||
}, | ||
deriveAp: function (Type) { | ||
return function(fa) { | ||
return this.chain(function (f) { | ||
return fa.chain(function (a) { | ||
return Type.of(f(a)); | ||
}); | ||
}); | ||
}; | ||
}, | ||
deriveMap: function (Type) { | ||
return function (f) { | ||
return this.chain(function (a) { | ||
return Type.of(f(a)); | ||
}); | ||
}; | ||
} | ||
}; |
@@ -1,7 +0,8 @@ | ||
var R = require('ramda'); | ||
var compose = require('ramda/src/compose'); | ||
var toString = require('ramda/src/toString'); | ||
var util = require('./internal/util'); | ||
module.exports = IO; | ||
var compose = R.compose; | ||
function IO(fn) { | ||
@@ -25,2 +26,13 @@ if (!(this instanceof IO)) { | ||
//chainRec | ||
IO.chainRec = IO.prototype.chainRec = function(f, i) { | ||
return new IO(function() { | ||
var state = util.chainRecNext(i); | ||
while (state.isNext) { | ||
state = f(util.chainRecNext, util.chainRecDone, state.value).fn(); | ||
} | ||
return state.value; | ||
}); | ||
}; | ||
IO.prototype.map = function(f) { | ||
@@ -54,3 +66,3 @@ var io = this; | ||
IO.prototype.toString = function() { | ||
return 'IO(' + R.toString(this.fn) + ')'; | ||
return 'IO(' + toString(this.fn) + ')'; | ||
}; |
@@ -1,5 +0,5 @@ | ||
var R = require('ramda'); | ||
var curryN = require('ramda/src/curryN'); | ||
module.exports = R.curryN(3, function lift2(f, a1, a2) { | ||
module.exports = curryN(3, function lift2(f, a1, a2) { | ||
return a1.map(f).ap(a2); | ||
}); |
@@ -1,5 +0,5 @@ | ||
var R = require('ramda'); | ||
var curryN = require('ramda/src/curryN'); | ||
module.exports = R.curryN(4, function lift3(f, a1, a2, a3) { | ||
module.exports = curryN(4, function lift3(f, a1, a2, a3) { | ||
return a1.map(f).ap(a2).ap(a3); | ||
}); |
@@ -1,2 +0,3 @@ | ||
var R = require('ramda'); | ||
var toString = require('ramda/src/toString'); | ||
var curry = require('ramda/src/curry'); | ||
@@ -47,3 +48,3 @@ var util = require('./internal/util.js'); | ||
Maybe.maybe = R.curry(function(nothingVal, justFn, m) { | ||
Maybe.maybe = curry(function(nothingVal, justFn, m) { | ||
return m.reduce(function(_, x) { | ||
@@ -83,2 +84,16 @@ return justFn(x); | ||
//chainRec | ||
Maybe.chainRec = Maybe.prototype.chainRec = function(f, i) { | ||
var res, state = util.chainRecNext(i); | ||
while (state.isNext) { | ||
res = f(util.chainRecNext, util.chainRecDone, state.value); | ||
if (Maybe.isNothing(res)) { | ||
return res; | ||
} | ||
state = res.value; | ||
} | ||
return Maybe.Just(state.value); | ||
}; | ||
// | ||
@@ -125,3 +140,3 @@ Just.prototype.datatype = Just; | ||
Just.prototype.toString = function() { | ||
return 'Maybe.Just(' + R.toString(this.value) + ')'; | ||
return 'Maybe.Just(' + toString(this.value) + ')'; | ||
}; | ||
@@ -128,0 +143,0 @@ |
@@ -1,2 +0,5 @@ | ||
var R = require('ramda'); | ||
var compose = require('ramda/src/compose'); | ||
var identity = require('ramda/src/identity'); | ||
var toString = require('ramda/src/toString'); | ||
var always = require('ramda/src/always'); | ||
@@ -43,6 +46,6 @@ | ||
Reader.ask = Reader(R.identity); | ||
Reader.ask = Reader(identity); | ||
Reader.prototype.toString = function() { | ||
return 'Reader(' + R.toString(this.run) + ')'; | ||
return 'Reader(' + toString(this.run) + ')'; | ||
}; | ||
@@ -58,3 +61,3 @@ | ||
ReaderT.lift = R.compose(ReaderT, R.always); | ||
ReaderT.lift = compose(ReaderT, always); | ||
@@ -92,10 +95,4 @@ ReaderT.ask = ReaderT(M.of); | ||
ReaderT.prototype.equals = function(that) { | ||
return this === that || | ||
this.run === that.run || | ||
R.equals(this.run().get(), that.run().get()); | ||
}; | ||
ReaderT.prototype.toString = function() { | ||
return 'ReaderT[' + M.name + '](' + R.toString(this.run) + ')'; | ||
return 'ReaderT[' + M.name + '](' + toString(this.run) + ')'; | ||
}; | ||
@@ -102,0 +99,0 @@ |
@@ -1,2 +0,3 @@ | ||
var R = require('ramda'); | ||
var toString = require('ramda/src/toString'); | ||
var equals = require('ramda/src/equals'); | ||
@@ -26,3 +27,3 @@ | ||
if (typeof x.concat != 'function') { | ||
throw new TypeError(R.toString(x) + ' must be a semigroup to perform this operation'); | ||
throw new TypeError(toString(x) + ' must be a semigroup to perform this operation'); | ||
} | ||
@@ -61,9 +62,9 @@ }); | ||
_Tuple.prototype.equals = function(that) { | ||
return that instanceof _Tuple && R.equals(this[0], that[0]) && R.equals(this[1], that[1]); | ||
return that instanceof _Tuple && equals(this[0], that[0]) && equals(this[1], that[1]); | ||
}; | ||
_Tuple.prototype.toString = function() { | ||
return 'Tuple(' + R.toString(this[0]) + ', ' + R.toString(this[1]) + ')'; | ||
return 'Tuple(' + toString(this[0]) + ', ' + toString(this[1]) + ')'; | ||
}; | ||
module.exports = Tuple; |
@@ -52,2 +52,44 @@ var R = require('ramda'); | ||
describe('ChainRec', function() { | ||
it('is a ChainRec', function() { | ||
var cTest = types.chainRec; | ||
var predicate = function(a) { | ||
return a.length > 5; | ||
}; | ||
var done = Either.of; | ||
var x = 1; | ||
var initial = [x]; | ||
var next = function(a) { | ||
return Either.of(a.concat([x])); | ||
}; | ||
assert.equal(true, cTest.iface(Either.of(1))); | ||
assert.equal(true, cTest.equivalence(Either, predicate, done, next, initial)); | ||
}); | ||
it('is stacksafe', function() { | ||
assert.equal(true, Either.of('DONE').equals(Either.chainRec(function(next, done, n) { | ||
if (n === 0) { | ||
return Either.of(done('DONE')); | ||
} else { | ||
return Either.of(next(n - 1)); | ||
} | ||
}, 100000))); | ||
}); | ||
it('responds to failure immediately', function() { | ||
assert.equal(true, Either.Left("ERROR").equals(Either.chainRec(function(/*next, done, n*/) { | ||
return Either.Left("ERROR"); | ||
}, 100))); | ||
}); | ||
it('responds to failure on next step', function() { | ||
assert.equal(true, Either.Left("ERROR").equals(Either.chainRec(function(next, done, n) { | ||
if (n === 0) { | ||
return Either.Left("ERROR"); | ||
} | ||
return Either.of(next(n - 1)); | ||
}, 100))); | ||
}); | ||
}); | ||
it('is a Monad', function() { | ||
@@ -54,0 +96,0 @@ jsv.assert(jsv.forall(eNatArb, types.monad.iface)); |
@@ -6,18 +6,33 @@ var R = require('ramda'); | ||
var Future = require('../src/Future'); | ||
var Promise = require('promise'); | ||
Future.prototype.equals = function(b) { | ||
this.fork(function(e1) { | ||
b.fork(function(e2) { | ||
assert.equal(e1, e2); | ||
}, function() { | ||
assert.fail(null, e1, 'Futures not equal: f1 failed, f2 did not', '==='); | ||
var self = this; | ||
return new Promise(function(resolve, reject) { | ||
self.fork(function(e1) { | ||
b.fork(function(e2) { | ||
try { | ||
assert.deepEqual(e1, e2); | ||
} catch (e) { reject(e); } | ||
resolve(); | ||
}, function() { | ||
try{ | ||
assert.fail(null, e1, 'Futures not equal: f1 failed, f2 did not', '==='); | ||
} catch (e) { reject(e); } | ||
reject(); | ||
}); | ||
}, function(v1) { | ||
b.fork(function() { | ||
try{ | ||
assert.fail(null, v1, 'Futures not equal: f1 succeeded, f2 did not', '==='); | ||
} catch (e) { reject(e); } | ||
reject(); | ||
}, function(v2) { | ||
try { | ||
assert.deepEqual(v1, v2); | ||
} catch (e) { reject(e); } | ||
resolve(); | ||
}); | ||
}); | ||
}, function(v1) { | ||
b.fork(function() { | ||
assert.fail(null, v1, 'Futures not equal: f1 succeeded, f2 did not', '==='); | ||
}, function(v2) { | ||
assert.equal(v1, v2); | ||
}); | ||
}); | ||
return true; | ||
}; | ||
@@ -27,6 +42,22 @@ | ||
it('should equal another future', function() { | ||
var f1 = Future.of(2); | ||
var f2 = Future.of(2); | ||
assert.equal(true, f1.equals(f2)); | ||
describe('Equal', function() { | ||
it('should equal another future', function() { | ||
var f1 = Future.of(2); | ||
var f2 = Future.of(2); | ||
return f1.equals(f2); | ||
}); | ||
it('should equal another future (async)', function() { | ||
var f1 = Future.of(2); | ||
var f2 = Future(function(rej, res) { | ||
setTimeout(res, 1, 2); | ||
}); | ||
return f1.equals(f2); | ||
}); | ||
it('should equal another future (non-primitive value)', function() { | ||
var f1 = Future.of([2,2]); | ||
var f2 = Future.of([2,2]); | ||
return f1.equals(f2); | ||
}); | ||
}); | ||
@@ -38,4 +69,6 @@ | ||
assert.equal(true, fTest.iface(f)); | ||
assert.equal(true, fTest.id(f)); | ||
assert.equal(true, fTest.compose(f, R.multiply(2), R.add(3))); | ||
return Promise.all([ | ||
fTest.id(f), | ||
fTest.compose(f, R.multiply(2), R.add(3)) | ||
]); | ||
}); | ||
@@ -49,3 +82,3 @@ | ||
assert.equal(true, aTest.iface(appA)); | ||
assert.equal(true, aTest.compose(appA, appU, appV)); | ||
return aTest.compose(appA, appU, appV); | ||
}); | ||
@@ -60,5 +93,7 @@ | ||
assert.equal(true, aTest.iface(app1)); | ||
assert.equal(true, aTest.id(app1, app2)); | ||
assert.equal(true, aTest.homomorphic(app1, R.add(3), 46)); | ||
assert.equal(true, aTest.interchange(app1, appF, 17)); | ||
return Promise.all([ | ||
aTest.id(app1, app2), | ||
aTest.homomorphic(app1, R.add(3), 46), | ||
aTest.interchange(app1, appF, 17), | ||
]); | ||
}); | ||
@@ -73,5 +108,49 @@ | ||
assert.equal(true, cTest.iface(f)); | ||
assert.equal(true, cTest.associative(f, f1, f2)); | ||
return cTest.associative(f, f1, f2); | ||
}); | ||
describe('ChainRec', function() { | ||
it('is a ChainRec', function() { | ||
var cTest = types.chainRec; | ||
var predicate = function(a) { | ||
return a.length > 5; | ||
}; | ||
var done = Future.of; | ||
var x = 1; | ||
var initial = [x]; | ||
var next = function(a) { | ||
return Future.of(a.concat([x])); | ||
}; | ||
assert.equal(true, cTest.iface(Future.of(1))); | ||
return cTest.equivalence(Future, predicate, done, next, initial); | ||
}); | ||
it('works when mixing sync and async Futures', function() { | ||
return Future.of('DONE').equals(Future.chainRec(function(next, done, n) { | ||
if (n === 0) { | ||
return Future.of(done('DONE')); | ||
} else if (n > 100 || n === 1) { | ||
return Future.of(next(n - 1)); | ||
} else { | ||
return new Future(function(rej, res) { setTimeout(res, 0, next(n - 1)); }); | ||
} | ||
}, 100000)); | ||
}); | ||
it('responds to failure immediately', function() { | ||
return Future.reject('ERROR').equals(Future.chainRec(function(/*next, done, n*/) { | ||
return Future.reject('ERROR'); | ||
}, 100)); | ||
}); | ||
it('responds to failure on next step', function() { | ||
return Future.reject('ERROR').equals(Future.chainRec(function(next, done, n) { | ||
if (n === 0) { | ||
return Future.reject('ERROR'); | ||
} | ||
return Future.of(next(n - 1)); | ||
}, 100)); | ||
}); | ||
}); | ||
it('is a Monad', function() { | ||
@@ -85,3 +164,3 @@ var mTest = types.monad; | ||
var result = Future.of(1).map(R.inc); | ||
assert.equal(true, Future.of(2).equals(result)); | ||
return Future.of(2).equals(result); | ||
}); | ||
@@ -94,3 +173,3 @@ | ||
var result = Future.of(1).chain(incInTheFuture); | ||
assert.equal(true, Future.of(2).equals(result)); | ||
return Future.of(2).equals(result); | ||
}); | ||
@@ -102,3 +181,3 @@ | ||
var f2 = function(val){ return Future.of(val + 3);}; | ||
assert.equal(true, Future.of(5).equals(f1.chainReject(f2))); | ||
return Future.of(5).equals(f1.chainReject(f2)); | ||
}); | ||
@@ -133,3 +212,3 @@ }); | ||
var result = f1.ap(Future.of(2)); | ||
assert.equal(true, Future.of(3).equals(result)); | ||
return Future.of(3).equals(result); | ||
}); | ||
@@ -409,2 +488,1 @@ | ||
}); | ||
@@ -60,2 +60,29 @@ var assert = require('assert'); | ||
describe('ChainRec', function() { | ||
it('is a ChainRec', function() { | ||
var cTest = types.chainRec; | ||
var predicate = function(a) { | ||
return a.length > 5; | ||
}; | ||
var done = Identity.of; | ||
var x = 1; | ||
var initial = [x]; | ||
var next = function(a) { | ||
return Identity.of(a.concat([x])); | ||
}; | ||
assert.equal(true, cTest.iface(Identity.of(1))); | ||
assert.equal(true, cTest.equivalence(Identity, predicate, done, next, initial)); | ||
}); | ||
it('is stacksafe', function() { | ||
assert.equal(true, Identity.of('DONE').equals(Identity.chainRec(function(next, done, n) { | ||
if (n === 0) { | ||
return Identity.of(done('DONE')); | ||
} else { | ||
return Identity.of(next(n - 1)); | ||
} | ||
}, 100000))); | ||
}); | ||
}); | ||
it('is a Monad', function() { | ||
@@ -62,0 +89,0 @@ var mTest = types.monad; |
var assert = require('assert'); | ||
var equals = require('ramda/src/equals'); | ||
var types = require('./types')(function(io1, io2) { | ||
return io1.runIO('x') === io2.runIO('x'); | ||
return io1.equals(io2); | ||
}); | ||
@@ -8,2 +9,6 @@ | ||
IO.prototype.equals = function(b) { | ||
return equals(this.runIO('x'), b.runIO('x')); | ||
}; | ||
function add(a) { | ||
@@ -89,2 +94,29 @@ return function(b) { return a + b; }; | ||
describe('ChainRec', function() { | ||
it('is a ChainRec', function() { | ||
var cTest = types.chainRec; | ||
var predicate = function(a) { | ||
return a.length > 5; | ||
}; | ||
var done = IO.of; | ||
var x = 1; | ||
var initial = [x]; | ||
var next = function(a) { | ||
return IO.of(a.concat([x])); | ||
}; | ||
assert.equal(true, cTest.iface(IO.of(1))); | ||
assert.equal(true, cTest.equivalence(IO, predicate, done, next, initial)); | ||
}); | ||
it('is stacksafe', function() { | ||
assert.equal(true, IO.of('DONE').equals(IO.chainRec(function(next, done, n) { | ||
if (n === 0) { | ||
return IO.of(done('DONE')); | ||
} else { | ||
return IO.of(next(n - 1)); | ||
} | ||
}, 100000))); | ||
}); | ||
}); | ||
it('is a Monad', function() { | ||
@@ -91,0 +123,0 @@ var mTest = types.monad; |
@@ -78,2 +78,46 @@ var R = require('ramda'); | ||
describe('ChainRec', function() { | ||
it('is a ChainRec', function() { | ||
var cTest = types.chainRec; | ||
var predicate = function(a) { | ||
return a.length > 5; | ||
}; | ||
var done = Maybe.of; | ||
var x = 1; | ||
var initial = [x]; | ||
var next = function(a) { | ||
return Maybe.of(a.concat([x])); | ||
}; | ||
assert.equal(true, cTest.iface(Maybe.of(1))); | ||
assert.equal(true, cTest.equivalence(Maybe, predicate, done, next, initial)); | ||
}); | ||
it('is stacksafe', function() { | ||
var a = Maybe.chainRec(function(next, done, n) { | ||
if (n === 0) { | ||
return Maybe.of(done('DONE')); | ||
} else { | ||
return Maybe.of(next(n - 1)); | ||
} | ||
}, 100000); | ||
console.log('a',a); | ||
assert.equal(true, Maybe.of('DONE').equals(a)); | ||
}); | ||
it('responds to failure immediately', function() { | ||
assert.equal(true, Maybe.Nothing().equals(Maybe.chainRec(function(/*next, done, n*/) { | ||
return Maybe.Nothing(); | ||
}, 100))); | ||
}); | ||
it('responds to failure on next step', function() { | ||
return Maybe.Nothing().equals(Maybe.chainRec(function(next, done, n) { | ||
if (n === 0) { | ||
return Maybe.Nothing(); | ||
} | ||
return Maybe.of(next(n - 1)); | ||
}, 100)); | ||
}); | ||
}); | ||
it('is a Monad', function() { | ||
@@ -80,0 +124,0 @@ var mTest = types.monad; |
@@ -8,2 +8,3 @@ var interfaces = { | ||
chain: ['map', 'ap', 'chain'], | ||
chainRec: ['map', 'ap', 'chain', 'chainRec'], | ||
monad: ['map', 'ap', 'chain', 'of'], | ||
@@ -97,2 +98,15 @@ extend: ['extend'], | ||
}, | ||
chainRec: { | ||
iface: correctInterface('chainRec'), | ||
equivalence: function (T, p, d, n, x) { | ||
return eq( | ||
T.chainRec(function(next, done, v) { | ||
return p(v) ? d(v).map(done) : n(v).map(next); | ||
}, x), | ||
(function step(v) { | ||
return p(v) ? d(v) : n(v).chain(step); | ||
}(x)) | ||
); | ||
} | ||
}, | ||
@@ -99,0 +113,0 @@ monad: { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
171645
51
2389
45
7