-
-
Save CrossEye/6603935 to your computer and use it in GitHub Desktop.
First Functor Fantasy Follow-up
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(global) { | |
// TODO: Make a tree structure from type objects' optional `parentKey` properties. Do a depth-first search of | |
// this tree instead of the simple linear search of `Object.keys`. | |
var types = {}; | |
global.Functor = function(config) { | |
types[config.key] = config; | |
}; | |
global.fmap = curry(function(f, obj) { | |
var type = types[obj.constructor.name], idx = -1, keys = Object.keys(types), len = keys.length; | |
if (!type || !type.test(obj)) {type = false;} | |
while (!type && ++idx < len) { | |
type = types[keys[idx]].test(obj) ? types[keys[idx]] : false; | |
} | |
if (!type) { | |
throw new TypeError("fmap called on unregistered type: " + obj) | |
} | |
return type.fmap(f, obj); | |
}); | |
}(this)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var makeTypeof = function(type) { | |
return function(obj) { | |
return type.prototype.isPrototypeOf(obj); | |
}; | |
}; | |
Functor({ | |
key: 'Array', | |
test: function(obj) { | |
return Object.prototype.toString.call(obj) === '[object Array]'; | |
}, | |
fmap: function(f, arr){ | |
return arr.map(function(x){return f(x);}); | |
} | |
}); | |
Functor({ | |
key: 'Function', | |
test: function(fn) { | |
return Object.prototype.toString.call(fn) === '[object Function]'; | |
}, | |
fmap: function(f, g) { | |
return compose(f, g); | |
} | |
}); | |
Functor({ | |
key: 'Maybe', | |
test: makeTypeof(Maybe), | |
fmap: function(f, maybe) { | |
return (maybe.val == null) ? maybe : Maybe(f(maybe.val)); | |
} | |
}); | |
Functor({ | |
key: 'Either', | |
test: makeTypeof(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) | |
console.log(fmap(plus1, {val: 5})); //=> TypeError: fmap called on unregistered type: [object Object] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment