-
-
Save richdouglasevans/ea96fb5fc8bb55d832a8a20f8c14d4ed to your computer and use it in GitHub Desktop.
The Monad example from the Fantasy Land series.
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
const Promise = require("fantasy-promises"); | |
const daggy = require("daggy"); | |
//- Regular `compose` - old news! | |
//+ compose :: (b -> c) | |
//+ -> (a -> b) | |
//+ -> a -> c | |
const compose = f => g => x => f(g(x)); | |
//- `chain`-sequencing `compose`, fancily | |
//- known as Kleisli composition - it's the | |
//- K in Ramda's "composeK"! | |
//+ mcompose :: Chain m | |
//+ => (b -> m c) | |
//+ -> (a -> m b) | |
//+ -> a -> m c | |
const mcompose = f => g => x => g(x).chain(f); | |
const Compose = daggy.tagged("Compose", ["f"]); | |
//- Remember, for semigroups: | |
//- concat :: Semigroup s => s -> s -> s | |
//- Replace s with (a -> a)... | |
//+ concat :: (a -> a) | |
//+ -> (a -> a) | |
//+ -> a -> a | |
Compose.prototype.concat = function(that) { | |
return Compose(x => this(that(x))); | |
}; | |
//- We need something that has no effect... | |
//- The `id` function! | |
//+ empty :: (a -> a) | |
Compose.empty = () => Compose(x => x); | |
const MCompose = T => { | |
const MCompose_ = daggy.tagged("f"); | |
//- Just as we did with Compose... | |
//+ concat :: Chain m | |
//+ => (a -> m a) | |
//+ -> (a -> m a) | |
//+ -> a -> m a | |
MCompose_.prototype.concat = function(that) { | |
return MCompose(x => that(x).chain(this)); | |
}; | |
//- So, we need empty :: (a -> m a) | |
//+ empty :: Chain m, Applicative m | |
//+ => (a -> m a) | |
MCompose_.empty = () => MCompose(M.of); | |
return MCompose_; | |
}; | |
// OUR APP | |
const rl = require("readline").createInterface({ | |
input: process.stdin, | |
output: process.stdout | |
}); | |
//+ prompt :: Promise String | |
const prompt = new Promise(res => rl.question("> ", res)); | |
//- We use "Unit" to mean "undefined". | |
//+ speak :: String -> Promise Unit | |
const speak = string => new Promise(res => res(console.log(string))); | |
//- Our entire asynchronous app! | |
//+ MyApp :: Promise String | |
const MyApp = | |
// Get the name... | |
speak("What is your name?").chain(_ => prompt).chain(name => | |
// Get the age... | |
speak("And what is your age?") | |
.chain(_ => prompt) | |
.chain( | |
age => | |
// Do the logic... | |
age > 30 | |
? speak("Seriously, " + name + "?!").chain(_ => | |
speak("You don't look a day over " + (age - 10) + "!") | |
) | |
: speak("Hmm, I can believe that!") | |
) | |
// Return the name! | |
.chain(_ => Promise.of(name)) | |
); | |
//- Our one little impurity: | |
// We run our program with a final | |
// handler for when we're all done! | |
MyApp.fork(name => { | |
// Do some database stuff... | |
// Do some beeping and booping... | |
console.log("FLATTERED " + name); | |
rl.close(); // Or whatever | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment