Skip to content

Commit

Permalink
Remove type-checking from chainRec
Browse files Browse the repository at this point in the history
Future.chainRec is no longer a "public" (documented)
function, as users are now able to achieve the same
using Future.chain. The only reason we keep it
around is for abstractions over Fantasy Land
ChainRec and Static Land ChainRec. Type checking
is merely a means to friendlier error messages and
friendly error messages are for users. Taking this
feature out increases the performance of code which
builds on ChainRec.
  • Loading branch information
Avaq committed May 12, 2017
1 parent 257a4cc commit 1a636d5
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 74 deletions.
46 changes: 6 additions & 40 deletions src/chain-rec.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,8 @@
import {Future, Core, isFuture} from './core';
import {Next, Done, isIteration} from './internal/iteration';
import {Future, Core} from './core';
import {Next, Done} from './internal/iteration';
import {Undetermined, Synchronous, Asynchronous} from './internal/timing';
import {show, showf, noop, partial1} from './internal/fn';
import {isFunction} from './internal/is';
import {invalidArgument, typeError} from './internal/throw';
import {FL, ordinal} from './internal/const';

function check$chainRec$future(m, f, i, x){
if(!isFuture(m)) typeError(
'Future.chainRec expects the function its given to return a Future every'
+ ' time it is called. The value returned from'
+ (ordinal[i] ? ` the ${ordinal[i]} call` : ` call ${i}`)
+ ' was not a Future.'
+ `\n Actual: ${show(m)}\n From calling: ${showf(f)}\n With: (Next, Done, ${show(x)})`
);
}

function check$chainRec$it(it, i){
if(!isIteration(it)) typeError(
'Future.chainRec expects the function its given to return a Future of an'
+ ' Iteration every time it is called. The Future returned from'
+ (ordinal[i] ? ` the ${ordinal[i]} call` : ` call ${i}`)
+ ' did not resolve to a member of Iteration.'
+ '\n You can create an uncomplete or complete Iteration using the next'
+ ' or done functions respectively. These are passed into your callback'
+ ' as first and second arguments.'
+ `\n Actual: Future.of(${show(it)})`
);
}
import {show, showf, noop} from './internal/fn';
import {FL} from './internal/const';

export function ChainRec(step, init){
this._step = step;
Expand All @@ -39,20 +14,17 @@ ChainRec.prototype = Object.create(Core);
ChainRec.prototype._fork = function ChainRec$_fork(rej, res){

const {_step, _init} = this;
let i = 0, timing = Undetermined, cancel = noop, state = Next(_init);
let timing = Undetermined, cancel = noop, state = Next(_init);

function resolved(it){
check$chainRec$it(it, i);
state = it;
i = i + 1;
timing = timing === Undetermined ? Synchronous : drain();
}

function drain(){
while(!state.done){
timing = Undetermined;
const m = _step(Next, Done, state.value);
check$chainRec$future(m, _step, i, state.value);
cancel = m._fork(rej, resolved);
if(timing !== Synchronous){
timing = Asynchronous;
Expand All @@ -72,14 +44,8 @@ ChainRec.prototype.toString = function ChainRec$toString(){
return `Future.chainRec(${showf(this._step)}, ${show(this._init)})`;
};

function chainRec$step(step, init){
return new ChainRec(step, init);
}

export function chainRec(step, init){
if(!isFunction(step)) invalidArgument('Future.chainRec', 0, 'be a Function', step);
if(arguments.length === 1) return partial1(chainRec$step, step);
return chainRec$step(step, init);
return new ChainRec(step, init);
}

Future[FL.chainRec] = chainRec;
35 changes: 1 addition & 34 deletions test/4.chain-rec.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,9 @@ import type from 'sanctuary-type-identifiers';

describe('chainRec()', () => {

it('is a curried binary function', () => {
it('is a binary function', () => {
expect(chainRec).to.be.a('function');
expect(chainRec.length).to.equal(2);
expect(chainRec(U.noop)).to.be.a('function');
});

it('throws TypeError when not given a function', () => {
const xs = [NaN, {}, [], 1, 'a', new Date, undefined, null];
const fs = xs.map(x => _ => chainRec(x, 1));
fs.forEach(f => expect(f).to.throw(TypeError, /Future/));
});

it('returns an instance of Future', () => {
Expand All @@ -36,32 +29,6 @@ describe('ChainRec', () => {

describe('#fork()', () => {

it('throws TypeError when the given function does not return a Future', () => {
const xs = [NaN, {}, [], 1, 'a', new Date, undefined, null];
const fs = xs.map(x => _ => chainRec((a, b, c) => (c, x), 1).fork(U.noop, U.noop));
fs.forEach(f => expect(f).to.throw(TypeError, /Future.*first call/));
});

it('throws TypeError when the given function does not always return a Future', () => {
const recur = (next, done, i) => i <= 6 ? of(next(i + 1)) : 'hello';
const f = _ => chainRec(recur, 1).fork(U.noop, U.noop);
expect(f).to.throw(TypeError, /Future.*6/);
});

it('throws TypeError when the returned Future does not contain an iteration', () => {
const xs = [null, '', {}, {value: 1, done: 1}];
const fs = xs.map(x => _ =>
chainRec((a, b, c) => of((c, x)), 1).fork(U.noop, U.noop)
);
fs.forEach(f => expect(f).to.throw(TypeError, /Future.*first call/));
});

it('throws TypeError when the returned Future does not always contain an iteration', () => {
const recur = (a, b, i) => i <= 6 ? of(a(i + 1)) : of('hello');
const f = _ => chainRec(recur, 1).fork(U.noop, U.noop);
expect(f).to.throw(TypeError, /Future.*6/);
});

it('does not break if the iteration does not contain a value key', () => {
const actual = chainRec((f, g, x) => (x, of({done: true})), 0);
return U.assertResolved(actual, undefined);
Expand Down

0 comments on commit 1a636d5

Please sign in to comment.