http://hackage.haskell.org/package/free in Control.Monad.Free.Free
allows one to get access to the "free monad" for any given Functor
. It does not, however, have a MonadFix
instance. Is this because such an instance cannot be written, or was it just left out?
If such an instance cannot be written, why not?
Well, inspired by the
MonadFix
instance forMaybe
, I tried this one (using the following definitions ofFree
):The laws are:
mfix (return . h) = return (fix h)
mfix (\x -> a >>= \y -> f x y) = a >>= \y -> mfix (\x -> f x y)
mfix (liftM h . f) = liftM h (mfix (f . h))
, for stricth
mfix (\x -> mfix (f x)) = mfix (\x -> f x x)
Purity is easy to prove -- but I came across a problem when trying to prove left shrinking:
but
So, at the very least, if the
Pure
andImpure
constructors are distinguishable, then my implementation ofmfix
does not satisfy the laws. I can't think of any other implementation, but that does not mean it does not exist. Sorry I couldn't illuminate further.Consider the description of what
mfix
does:The word "executes", in the context of
Free
, means creating layers of theFunctor
. Thus, "only once" means that in the result of evaluatingmfix f
, the values held inPure
constructors must fully determine how many layers of the functor are created.Now, say we have a specific function
once
that we know will always only create a singleFree
constructor, plus however manyPure
constructors are needed to hold the leaf values. The output of 'once', then, will be only values of typeFree f a
that are isomorphic to some value of typef a
. With this knowledge, we can un-Free
the output ofonce
safely, to get a value of typef a
.Now, note that because
mfix
is required to "execute the action only once", the result ofmfix once
should, for a conforming instance, contain no additional monadic structure thanonce
creates in a single application. Thus we can deduce that the value obtained frommfix once
must also be isomorphic to a value of typef a
.Given any function with type
a -> f a
for someFunctor
f
, we can wrap the result with a singleFree
and get a function of typea -> Free f a
which satisfies the description ofonce
above, and we've already established that we can unwrap the result ofmfix once
to get a value of typef a
back.Therefore, a conforming instance
(Functor f) => MonadFix (Free f)
would imply being able to write, via the wrapping and unwrapping described above, a functionffix :: (Functor f) => (a -> f a) -> f a
that would work for all instances ofFunctor
.That's obviously not a proof that you can't write such an instance... but if it were possible,
MonadFix
would be completely superfluous, because you could just as easily writeffix
directly. (And I suppose reimplement it asmfix
with aMonad
constraint, usingliftM
. Ugh.)No, it cannot be written in general, because not every Monad is an instance of MonadFix. Every Monad can be implemented using the FreeMonad underneath. If you could implement the MonadFix for Free, then you would be able to derive MonadFix from any Monad, which is not possible. But of course, you can define a FreeFix for the MonadFix class.
I think it might look somehow like this, but this is just a 3rd guess (still not tested):
The idea is that m is a Monad that lacks an implementation for mfix; so mfix needs to be a parameter when FreeFix will be reduced.