Skip to content

Instantly share code, notes, and snippets.

@gunar
Last active October 27, 2016 14:27
Show Gist options
  • Save gunar/8c6e0c3aa051b32091b7b37ab5f676c3 to your computer and use it in GitHub Desktop.
Save gunar/8c6e0c3aa051b32091b7b37ab5f676c3 to your computer and use it in GitHub Desktop.
Algebraic Data Types
// Inspired by [Brian Lonsdorf - Oh Composable World! - YouTube](https://www.youtube.com/watch?v=SfWR3dKnFIo)
// Inspired by [Mostly adequate guide to FP](https://github.com/MostlyAdequate/mostly-adequate-guide)
const Box = function(x) {
this.__value = x;
}
Box.of = x => new Box(x)
Box.prototype.map = function (f) {
return Box.of(f(this.__value))
}
Box.prototype.chain = function (f) {
return f(this.__value)
}
Box.prototype.concat = function (box) {
return box.map(x => this.__value.concat(x))
// or
return box.chain(x => Box.of(this.__value.concat(x)))
}
Box.prototype.ap = function (x) {
return x.map(this.__value)
}
Box.prototype.reduce = function (f, acc) {
const arr = []
for (let i = 0; i <= this.__value; i++) {
arr.push(i);
}
return arr.reduce(f, acc)
}
Box.prototype.toString = function() { return `Box(${this.__value})` }
console.log('Functor: ' +
Box.of(20).map(x => x / 2))
// Box(10)
console.log('Monad: ' +
Box.of(true).chain(x => Box.of(!x)))
// Box(false)
console.log('Monoid: ' +
Box.of('small').concat(Box.of('pox')))
// Box('smallpox')
console.log('Applicative: ' +
Box.of(x => x + 1).ap(Box.of(2)))
// Box(3)
console.log('Foldable: ' +
Box.of(3).reduce((acc, x) => acc + x, 0))
// I needed to implement Either in order to try out Traversable
// Either
function Either(x) {
this.__value = x;
}
Either.of = x => new Either(x)
Either.fromNullable = function (x) {
return x
? Right.of(x)
: Left.of(x)
}
Either.prototype.toString = function() { return `Either(${this.__value})` }
// Left
function Left(x) {
this.__value = x;
}
Left.of = x => new Left(x)
Left.prototype.chain = function (f) {
return f(this.__value)
}
Left.prototype.toString = function() { return `Left(${this.__value})` }
// Right
function Right(x) {
this.__value = x;
}
Right.of = x => new Right(x)
Right.prototype.map = function (f) {
return Right.of(f(this.__value))
}
Right.prototype.chain = function (f) {
return f(this.__value)
}
Right.prototype.toString = function() { return `Right(${this.__value})` }
Box.prototype.traverse = function (f, of) {
return f(this.__value).map(of)
}
console.log('Traversable: ' +
Box.of(3).traverse(Either.fromNullable, Box.of))
function eitherToBox(either) {
return either.chain(x => Box.of(x))
}
console.log('Natural transformation: ',
eitherToBox(Either.fromNullable(null)).toString())
// Box(null)
@dtipson
Copy link

dtipson commented Oct 27, 2016

What's the theory behind the implementation of reduce there? For FL it's just:

Identity.prototype.reduce = function(f, acc) {
return f(acc, this.value);
};

There's also .fold as an alternative (which for Identity can be implemented as just an alias for .chain, but also has a binary implementation for Either to fold out of the type).

Once you have fold, you can create an eithertoBox that just does either.fold(Box.of,Box.of) (i.e. putting either the left or the right value in a box.) Using chain to remove things from a type is a bad idea (as it's not always, and often not just f(this.value), or alias to fold).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment