Skip to content

Commit

Permalink
implement ChainRec for Future
Browse files Browse the repository at this point in the history
  • Loading branch information
safareli committed Sep 8, 2016
1 parent 7b285f2 commit 3e1020f
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 0 deletions.
83 changes: 83 additions & 0 deletions src/Future.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,89 @@ Future.prototype.chain = function(f) { // Sorella's:
}.bind(this));
};

var chainRecFork = function(t, rej, res) {
var isSync = false;
t.fork(function(v) {
var r = rej(v, !isSync);
isSync = true;
return r;
}, function(v) {
var r = res(v, !isSync);
isSync = true;
return r;
});
if (isSync) {
return { isSync: true };
} else {
isSync = true;
return { isSync: false };
}
};

var chainRecNext = function(v) {
return function(onNext/*, onDone*/){
return onNext(v);
};
};

var chainRecDone = function(v) {
return function(onNext, onDone){
return onDone(v);
};
};

// chainRec
Future.chainRec = Future.prototype.chainRec = function(f, i) {
return new Future(function(reject, resolve) {
chainRecFork(
f(chainRecNext, chainRecDone, i),
function(z/*, isSync*/) {
return reject(z);
},
function(fold, isSync) {
return fold(
function(v2) {
if (isSync === false) {
Future.chainRec(f, v2).fork(reject, resolve);
return;
}
var state = { loop: true, arg: v2 };
var onReject = function(z/*, isSync*/) {
state.loop = false;
reject(z);
};
var onResolve = function(fold, isSync) {
return fold(
function(v3) {
state = { loop: isSync, arg: v3 };
if (isSync === false) {
Future.chainRec(f, v3).fork(reject, resolve);
}
},
function(v) {
state = { loop: false};
resolve(v);
}
);
};
while (state.loop) {
var forkRes = chainRecFork(
f(chainRecNext, chainRecDone, state.arg),
onReject,
onResolve
);
if (forkRes.isSync === false) {
state = { loop: false};
}
}
},
resolve
);
}
);
});
};

// chainReject
// Like chain but operates on the reject instead of the resolve case.
//:: Future a, b => (a -> Future c) -> Future c
Expand Down
44 changes: 44 additions & 0 deletions test/future.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,50 @@ describe('Future', function() {
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('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('fail Immediately', function() {
return Future.reject('ERROR').equals(Future.chainRec(function(/*next, done, n*/) {
return Future.reject('ERROR');
}, 100));
});

it('fail 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() {
var mTest = types.monad;
var f = Future.of(null);
Expand Down

0 comments on commit 3e1020f

Please sign in to comment.