Skip to content

Instantly share code, notes, and snippets.

@djfm
Created July 25, 2016 11:25
Show Gist options
  • Save djfm/0fcc46672b17b9bcb232874e07731d94 to your computer and use it in GitHub Desktop.
Save djfm/0fcc46672b17b9bcb232874e07731d94 to your computer and use it in GitHub Desktop.
incomplete but fun monad implementation attempt
const chai = require('chai');
chai.should();
const listMonad = {
// make :: a -> m a
// make :: a -> [a]
make: (...args) => args,
// bind :: m a -> (a -> m b) -> m b
// bind :: [a] -> (a -> [b]) -> [b]
bind: someList => listReturningFunction =>
[].concat(...someList.map(listReturningFunction)),
};
const curry = func => {
if (func.length < 2) {
return func;
}
const curryDeep = argsCollected => {
if (argsCollected.length >= func.length) {
return func(...argsCollected);
}
return (...args) =>
curryDeep(argsCollected.concat(args));
};
return curryDeep([]);
};
describe('"curry"', () => {
it('does not alter a function of zero arguments',
() => curry(() => 1)().should.equal(1)
);
it('does not alter a function of one argument',
() => curry(x => x)(1).should.equal(1)
);
it('turns sub(a, b) into sub(a)(b)',
() => curry((a, b) => a - b)(1)(1).should.equal(0)
);
it('can curry a curried function',
() => curry(curry((a, b) => a - b))(1)(1).should.equal(0)
);
it('treats (a, b) like (a)(b)',
() => {
curry((a, b, c) => a - b + c)(1)(2)(3).should.equal(2);
curry((a, b, c) => a - b + c)(1, 2)(3).should.equal(2);
curry((a, b, c) => a - b + c)(1)(2, 3).should.equal(2);
}
);
});
const withMonad = monad =>
funcOfMonadHelpers => {
// inject :: ((...a) -> b) -> (...m a) -> m b
const inject = funcOfRegularValues =>
(monadValue, ...nextMonadValues) =>
monad.bind(monadValue)(
nextMonadValues.length > 0 ?
value => inject(
curry(funcOfRegularValues)(value)
)(...nextMonadValues) :
curry(funcOfRegularValues)
)
;
return funcOfMonadHelpers({ inject });
}
;
const mulBy2 = list => withMonad(listMonad)(
({ inject }) => inject(x => x * 2)(list)
);
describe('"mulBy2"', () => {
specify('[1, 2] => [2, 4]', () =>
mulBy2([1, 2]).should.deep.equal([2, 4])
);
});
const cartesianProduct = (listA, listB) =>
withMonad(listMonad)(
({ inject }) => inject(
(a, b) => ({ a, b })
)(listA, listB)
)
;
describe('"cartesianProduct"', () => {
specify('[1, 2] x [3, 4] = [[1, 3], [1, 4], [2, 3], [2, 4]]', () =>
cartesianProduct([1, 2], [3, 4]).should.deep.equal(
[{ a: 1, b: 3 }, { a: 1, b: 4 }, { a: 2, b: 3 }, { a: 2, b: 4 }]
)
);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment