Deriving free monads from value-level functors
Or, I have finally cured insomnia. Call CNN.
1 Functors and Free Monads
An instance of F should contain a function called _map constrained by whichever base type is being functor-ized.
Mu is the free monad. I like calling it Mu for two reasons: because calling it “free” is confusing and not informative; and because Mu behaves like the type-level analog to the Y-combinator, called Mu.
These two are different names for the same thing, mostly for aesthetic reasons. You’ll see.
bind :: F f -> Mu f a -> (a -> Mu f b) -> Mu f b
bind i arg fn = case arg of
Term t -> fn t
Cont k -> Cont (map (bind' fn) k)
where map = _map i
bind' = flip (bind i)This is the cool part. bind is defined for any type which is wrapped by Mu, and for a type to be wrapped by Mu it must instantiate our F type. Thus we can ask for an “instance” argument, called i here.
2 An example: re-creating the Maybe monad.
Think of how much time we have collectively lost to massive failure due to null pointer errors. Now behold how simple optional types are to implement, and weep:
Now for the fun part. I will create an F instance for Optional along with some convenience functions to use in monadic computations. While I don’t do it here, this could be derived automatically.
fOptional :: F Optional
fOptional = F {
_map = \f x -> case x of
Nil -> Nil
Some s -> Some $ f s
}
nil = Cont Nil
some = TermIn ~12 lines of real code, I have created a Maybe clone and proven it is a functor. As a result all the remaining code necessary to compose a monad has been derived automatically.
Since this was a free monad, the only remaining code is that to “run” the monadic computation built up using unit and bind.
3 Tests!
Without further ado, here is some example code written in our Optional monad.
testOptional1 = some 5 >>= \a ->
some 6 >>= \b ->
yield $ a * b
where (>>=) = bind fOptional
testOptional2 = some 5 >>= \a ->
nil >>= \b ->
some 6 >>= \c ->
yield $ a * c
where (>>=) = bind fOptionalTry it out for yourself to see the results.