Created
December 4, 2013 23:01
-
-
Save puffnfresh/7797202 to your computer and use it in GitHub Desktop.
Promises as functors are useful.
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 Promise = require('fantasy-promises'), | |
fl = "https://api.github.com/repos/fantasyland/", | |
// Pretend this URL is given by the user, not setTimeout | |
userInput = new Promise(function(resolve) { | |
setTimeout(function() { | |
resolve(fl + "fantasy-states"); | |
}, Math.random() * 500); | |
}), | |
// We should be able to reuse this function. Functors compose, so | |
// we map then map to change a value. | |
watchers = compose(map)(map)(function(o) { | |
return o.watchers; | |
}); | |
// Uses node.js to send a http request, JSON deserialises the response | |
function makeRequest(url) { | |
var http = require('https'), | |
urlOptions = require('url').parse(url); | |
urlOptions['headers'] = { 'User-Agent': 'node.js' }; | |
return new Promise(function(resolve) { | |
var body = ''; | |
http.get(urlOptions, function(response) { | |
response.on('data', function(chunk) { | |
body += chunk; | |
}); | |
response.on('end', function() { | |
resolve(JSON.parse(body)); | |
}); | |
}); | |
}); | |
} | |
// Runs a promise, prints the results | |
function run(p) { | |
p.fork(function(result) { | |
console.log(result); | |
}); | |
} | |
// identity(a) = a | |
function identity(a) { | |
return a; | |
} | |
// compose(f)(g)(x) = f(g(x)) | |
function compose(f) { | |
return function(g) { | |
return function(x) { | |
return f(g(x)); | |
}; | |
}; | |
} | |
// map(f)(o) == o.map(f) | |
function map(f) { | |
return function(o) { | |
return o.map(f); | |
}; | |
} | |
// flatten(o) == o.chain(identity) | |
function flatten(o) { | |
return o.chain(identity); | |
} | |
// Id constructor - trivial functor | |
function Id(value) { | |
this.value = value; | |
} | |
Id.prototype.map = function(f) { | |
return new Id(f(this.value)); | |
}; | |
// Id of promise | |
run(watchers(new Id(fl + "fantasy-options").map(makeRequest)).value); | |
// Array of promises | |
watchers([fl + "fantasy-promises", fl + "fantasy-lenses"].map(makeRequest)).forEach(run); | |
// Promise of promise | |
run(flatten(watchers(userInput.map(makeRequest)))); | |
// Notice that most of the above code is reused for different | |
// structures. The only common operation is `map`. This means we're | |
// working across Functors. | |
// We could make the Promise case a little more direct but then we | |
// wouldn't have the same function which worked over many of our | |
// structures. I love code reuse. | |
// Sadly, Promises/A+ doesn't have a Functor, because it only has | |
// `then` which is almost like `map` but has a special case for | |
// functions which return an object with a `then` property. In other | |
// words, it's not parametric. We might be able to work around that by | |
// wrapping results or something. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment