I think the issue is just that the error
message is misleading. Let's just focus on MonadFix Maybe
. The argument to mfix
can be one of four things.
It can be strict in the input: f _|_ = _|_
or "f
needs to evaluate its input to decide whether it will return Nothing
or Just
"
\x -> if x then Nothing else Just True
\x -> x `seq` Nothing
\x -> x `seq` Just x
It can be const Nothing
.
It can be Just . f
where f
is not strict.
Just . (1:)
It can be Just . f
where f
is strict.
Just
If the function is strict, then the whole thing blows up in an infinite loop (just like fix
), and the error isn't seen because we don't know whether we would have had a Nothing
or a Just
. If it is const Nothing
, the function never actually tries to evaluate the error
and nothing happens. If it is Just . f
and f
is not strict, then it's just Just $ fix f
(as per the laws: mfix $ return . f = return $ fix f
). And, if f
is strict, we get Just _|_
(again, per the laws). Notice that we never see the error
triggered.
Similar reasoning works for MonadFix (MaybeT m)
. I think this time it's better just with an example:
runMaybeT $ mfix $ \x -> MaybeT $
(x `seq` Nothing) :
Nothing :
(Just (1:x)) :
(Just x) :
(x `seq` [])
Each of the four cases I listed above are in that list. The first element of the result is an infinite loop. The second is Nothing
. The third is repeat 1
, and the fourth is Just
an infinite loop. Trying to access the "elements" beyond that triggers another infinite loop, this time caused by []
's MonadFix
and not MaybeT
's. Again, I don't believe it's possible to trigger the error
, because the function would have to force the argument after already deciding that the result was Nothing
.
Nothing
"? – SymondsMaybeT
implementation ofmfix
to throw its error, witnessing the partiality? I tried, and returningNothing
simply wasn't enough; e.g.mfix (\x -> MaybeT [Nothing]) :: MaybeT [] ()
works just fine. On the other hand, it is easy to observe your implementation being partial; e.g.mfix' (\x -> MaybeT [Nothing]) :: MaybeT [] ()
(the exact same call, but withmfix'
instead ofmfix
) never returns. – Crinummfix :: Maybe a -> m (Maybe a)
can only be one of 2 kinds of functions: it can either pattern match over the argument in which casemfix
evaluates to bottom, or it is a constant function in which case wemfix
just evaluates to the value. The implementation I present above wraps the function in a pattern match, thereby makingmfix
always a bottom. – Kinesiology