Skip to content

Instantly share code, notes, and snippets.

@i-am-tom
Created June 5, 2017 20:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save i-am-tom/01dfe6a02b5e7664c6d353c69399cd16 to your computer and use it in GitHub Desktop.
Save i-am-tom/01dfe6a02b5e7664c6d353c69399cd16 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('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