Skip to content

Instantly share code, notes, and snippets.

@CrossEye
Last active July 26, 2020 19:59
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save CrossEye/5715961 to your computer and use it in GitHub Desktop.
Save CrossEye/5715961 to your computer and use it in GitHub Desktop.
First Functor Fantasy
var curry = function(fn) {
var arity = fn.length;
var f = function(args) {
return function () {
var newArgs = (args || []).concat([].slice.call(arguments, 0));
if (newArgs.length >= arity) {
return fn.apply(this, newArgs);
}
else {return f(newArgs);}
};
};
return f([]);
};
var compose = function(f, g) {
return function() {
return f.call(this, (g.apply(this, arguments)));
};
};
var Maybe = function(val) {
if (!(this instanceof Maybe)) {return new Maybe(val);}
this.val = val;
};
var Either = function(left, right) {
if (!(this instanceof Either)) {return new Either(left, right);}
this.left = left;
this.right = right;
};
(function(global) {
var types = function(obj) {
throw new TypeError("fmap called on unregistered type: " + obj);
};
// inefficient as hell, but as long as there aren't too many types....
global.Functor = function(type, defs) {
var oldTypes = types;
types = function(obj) {
if (type.prototype.isPrototypeOf(obj)) {
return defs;
}
return oldTypes(obj);
}
};
global.fmap = curry(function(f, obj) {
return types(obj).fmap(f, obj);
});
}(this));
// These need to be registered from general to specific.
// The alternative I can see is to use a prototype-based solutions as done in:
// https://github.com/loop-recur/typeclasses/blob/master/functor.js
Functor(Array, {fmap: function(f, arr){
return arr.map(function(x){return f(x);});
}});
Functor(Function, {fmap: function(f, g) {
return compose(f, g);
}});
Functor(Maybe, {fmap: function(f, maybe) {
return (maybe.val == null) ? maybe : Maybe(f(maybe.val));
}});
Functor(Either, {fmap: function(f, either) {
return (either.right == null) ? Either(f(either.left), null) : Either(either.left, f(either.right));
}});
var plus1 = function(n) {return n + 1;};
var times2 = function(n) {return n * 2;};
console.log(fmap(plus1, [2, 4, 6, 8])); //=> [3, 5, 7, 9]
console.log(fmap(plus1, Maybe(5))); //=> Maybe(6)
console.log(fmap(plus1, Maybe(null))); //=> Maybe(null)
console.log(fmap(plus1, times2)(3)); //=> 7 (= 3 * 2 + 1)
console.log(fmap(plus1, Either(10, 20))); //=> Either(10, 21)
console.log(fmap(plus1, Either(10, null))); //=> Either(11, null)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment