Skip to content

Instantly share code, notes, and snippets.

@richdouglasevans
Forked from i-am-tom/monad.js
Last active November 1, 2018 09:00
Show Gist options
  • Save richdouglasevans/ea96fb5fc8bb55d832a8a20f8c14d4ed to your computer and use it in GitHub Desktop.
Save richdouglasevans/ea96fb5fc8bb55d832a8a20f8c14d4ed to your computer and use it in GitHub Desktop.
The Monad example from the Fantasy Land series.
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