How can we prove that the continuation monad has no valid instance of MonadFix?
Why can't there be an instance of MonadFix for the continuation monad?
1.5k Views Asked by Petr At
2
There are 2 best solutions below
4
On
Well actually, it's not that there can't be a MonadFix instance, just that the library's type is a bit too constrained. If you define ContT over all possible rs, then not only does MonadFix become possible, but all instances up to Monad require nothing of the underlying functor :
newtype ContT m a = ContT { runContT :: forall r. (a -> m r) -> m r }
instance Functor (ContT m) where
fmap f (ContT k) = ContT (\kb -> k (kb . f))
instance Monad (ContT m) where
return a = ContT ($a)
join (ContT kk) = ContT (\ka -> kk (\(ContT k) -> k ka))
instance MonadFix m => MonadFix (ContT m) where
mfix f = ContT (\ka -> mfixing (\a -> runContT (f a) ka<&>(,a)))
where mfixing f = fst <$> mfix (\ ~(_,a) -> f a )
Consider the type signature of
mfixfor the continuation monad.Here's the proof that there's no pure inhabitant of this type.
As you can see the problem is that both
fandkexpect a value of typeaas an input. However, there's no way to conjure a value of typea. Hence, there's no pure inhabitant ofmfixfor the continuation monad.Note that you can't define
mfixrecursively either becausemfix f k = mfix ? ?would lead to an infinite regress since there's no base case. And, we can't definemfix f k = f ? ?ormfix f k = k ?because even with recursion there's no way to conjure a value of typea.But, could we have an impure implementation of
mfixfor the continuation monad? Consider the following.The question that arises is how to apply
ftox'. Normally, we'd do this using a recursive let expression, i.e.let x' = f x'. However,x'is not the return value off. Instead, the continuation given tofis applied tox'. To solve this conundrum, we create an empty mutable variablem, lazily read its valuex, and applyftox. It's safe to do so becausefmust not be strict in its argument. Whenfeventually calls the continuation given to it, we store the resultx'inmand apply the continuationktox'. Thus, when we finally evaluatexwe get the resultx'.The above implementation of
mfixfor the continuation monad looks a lot like the implementation ofmfixfor theIOmonad.Note, that in the implementation of
mfixfor the continuation monad we usedreadMVarwhereas in the implementation ofmfixfor theIOmonad we usedtakeMVar. This is because, the continuation given tofcan be called multiple times. However, we only want to store the result given to the first callback. UsingreadMVarinstead oftakeMVarensures that the mutable variable remains full. Hence, if the continuation is called more than once then the second callback will block indefinitely on theputMVaroperation.However, only storing the result of the first callback seems kind of arbitrary. So, here's an implementation of
mfixfor the continuation monad that allows the provided continuation to be called multiple times. I wrote it in JavaScript because I couldn't get it to play nicely with laziness in Haskell.Here's the equivalent Haskell code, sans the implementation of
mfix.Notice that this looks a lot like the list monad.
This makes sense because after all the continuation monad is the mother of all monads. I'll leave the verification of the
MonadFixlaws of my JavaScript implementation ofmfixas an exercise for the reader.