问题主要在标题中。似乎可以为任何monadic计算定义mfix
,即使它可能有所不同:
mfix :: (a -> m a) -> m a
mfix f = fix (join . liftM f)
这种结构有什么问题?另外,为什么Monad
和MonadFix
类型类别是分开的(即什么类型的实例为Monad
而不是MonadFix
?)
答案 0 :(得分:16)
<{3}}
mfix (\x -> a >>= \y -> f x y) = a >>= \y -> mfix (\x -> f x y)
特别是这意味着
mfix (\x -> a' >> f x) = a' >> mfix f
这意味着mfix
内的monadic动作必须只评估一次。这是您的版本无法满足的MonadFix
的主要属性之一。
考虑这个创建循环可变列表的示例(由于可变性,我们忽略了在没有mfix
的情况下可以执行此操作的事实):
import Control.Monad
import Control.Monad.Fix
import Data.IORef
data MList a = Nil | Cons a (IORef (MList a))
mrepeat :: a -> IO (MList a)
mrepeat x = mfix (liftM (Cons x) . newIORef)
main = do
(Cons x _) <- mrepeat 1
print x
对于mfix
的变体,对mrepeat
的调用永远不会完成,因为您无限期地使用newIORef
调用内部部分。
答案 1 :(得分:6)
您对mfix
的定义不能保证等同于标准的定义。事实上,至少在monad列表中它更严格:
> take 1 $ mfix (\x -> [1,x])
[1]
> let mfix2 :: Monad m => (a -> m a) -> m a; mfix2 f = fix (join . liftM f)
> take 1 $ mfix2 (\x -> [1,x])
Interrupted.