I\'ve been learning about functional programming and have come across Monads, Functors and Applicatives.
From my understanding the following definitions apply:
Promise
is (a lot like) a monad because then
is overloaded.
When we use Promise.then(func), we are passing the Promise(i.e. C[A]) a function which normally has signature A => B and return another Promise (i.e. C[B]). So my thinking was that a Promise would only be a Functor and not a Monad as func returns B and not C[B].
this is true for then(Promise, Func) : Promise
(if you'll excuse my pseudocode for javascript types, I'll be describing functions as though this
were the first argument)
the Promise API supplies another signature for then
though, then(Promise, Func>) : Promise
. This version obviously fits the signature for monadic bind (>>=
). Try it out yourself, it works.
however, fitting the signature for a monad doesn't mean that Promise is a monad. it also needs to satisfy the algebraic laws for monads.
the laws a monad must satisfy are the law of associativity
(m >>= f) >>= g ≡ m >>= ( \x -> (f x >>= g) )
and the laws of left and right identity
(return v) >>= f ≡ f v
m >>= return ≡ m
in JavaScript:
function assertEquivalent(px, py) {
Promise.all([px, py]).then(([x, y]) => console.log(x === y));
}
var _return = x => Promise.resolve(x)
Promise.prototype.bind = Promise.prototype.then
var p = _return("foo")
var f = x => _return("bar")
var g = y => _return("baz")
assertEquivalent(
p.bind(f).bind(g),
p.bind(x => f(x).bind(g))
);
assertEquivalent(
_return("foo").bind(f),
f("foo")
);
assertEquivalent(
p.bind(x => _return(x)),
p
);
I think anyone familiar with promises can see that all of these should be true, but feel free to try it yourself.
because Promise is a monad, we can derive ap
and get an applicative out of it as well, giving us some very nice syntax with a little ill-advised hackery:
Promise.prototype.ap = function (px) {
return this.then(f => px.then(x => f(x)));
}
Promise.prototype.fmap = function(f) {
return this.then(x => f(x));
}
// to make things pretty and idiomatic
Function.prototype.doFmap = function(mx) {
return mx.fmap(this);
}
var h = x => y => x + y
// (h <$> return "hello" <*> return "world") >>= printLn
h.doFmap(_return("hello, ")).ap(_return("world!")).bind(console.log)