Skip to content

Instantly share code, notes, and snippets.

@dypsilon
Last active September 28, 2019 16:09
Show Gist options
  • Select an option

  • Save dypsilon/437b84fd31b255b7ef2e47ceaa91245a to your computer and use it in GitHub Desktop.

Select an option

Save dypsilon/437b84fd31b255b7ef2e47ceaa91245a to your computer and use it in GitHub Desktop.
A simple implementation of the identity monad without any dependencies. Including testing the algebraic laws.
/**
* The Identity monad is a monad that does not embody any computational strategy.
* It simply applies the bound function to its input without any modification.
* Computationally, there is no reason to use the Identity monad instead of the much simpler
* act of simply applying functions to their arguments.
*
* The purpose of the Identity monad is its fundamental role in the theory of monad transformers.
* Any monad transformer applied to the Identity monad yields a non-transformer version of that monad.
*
* source: https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Identity.html
*/
class Identity {
constructor(x) {
this.x = x;
}
}
Identity.prototype.of = Identity.of = x => new Identity(x)
Identity.prototype.chain = function(f) {
return f(this.x);
};
Identity.prototype.map = function(f) {
return this.of(f(this.x));
}
Identity.prototype.ap = function(m) {
return m.map(this.x);
}
Identity.prototype.inspect = function() {
return `Identity(${this.x})`;
}
Identity.prototype.equals = function(expected) {
return expected instanceof Identity && this.x === expected.x;
}
module.exports = Identity;
/**
* These are some very simple tests.
* You can find extensive law testing library here: https://github.com/ramda/ramda-fantasy/blob/master/test/types.js
* It has many dependencies, though.
*/
const Identity = require('./identity');
const eq = (test, expected, actual) => {
if(!expected.equals(actual)) {
throw new Error(`Test failed: ${ test }`);
}
};
{ // Functor
const u = Identity.of(1);
const f = (x) => x + 1;
const g = (x) => x + 2;
eq('Functor: identity', u.map(x => x), u);
eq('Functor: composition', u.map(x => f(g(x))), u.map(g).map(f));
}
{ // Apply
const a = Identity.of((x) => x + 1);
const u = Identity.of((x) => x + 2);
const v = Identity.of(1);
eq(
'Apply: composition',
a.map(f => g => x => f(g(x))).ap(u).ap(v),
a.ap(u.ap(v))
)
}
{ // Applicative
const a = Identity;
const v = Identity.of(1);
const f = x => x + 1;
const u = Identity.of(x => x + 2)
const x = 2;
const y = 4;
eq('Applicative: identity', a.of(x => x).ap(v), v);
eq('Applicative: homomorphism', a.of(f).ap(a.of(x)), a.of(f(x)));
eq('Applicative: interchange', u.ap(a.of(y)), a.of((f) => f(y)).ap(u));
}
{ // Chain
const m = Identity.of(3);
const f = x => Identity.of(x + 1);
const g = x => Identity.of(x + 2);
eq('Chain: associativity', m.chain(f).chain(g), m.chain(x => f(x).chain(g)));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment