13

问题主要在标题中。似乎mfix可以为任何一元计算定义,即使它可能会发散:

mfix :: (a -> m a) -> m a
mfix f = fix (join . liftM f)

这种结构有什么问题?另外,为什么 theMonadMonadFixtypeclasses 是分开的(即什么类型有一个实例Monad但没有 of MonadFix)?

4

2 回答 2

16

左收缩(或紧缩)定律说

mfix (\x -> a >>= \y -> f x y)  =  a >>= \y -> mfix (\x -> f x y)

这尤其意味着

mfix (\x -> a' >> f x)  =  a' >> mfix f

这意味着内部的一元动作mfix必须只计算一次。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无限期地调用内部部分。

于 2014-09-12T19:30:33.793 回答
6

mfix不保证您的定义等同于标准定义。事实上,至少在 list 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.
于 2014-09-12T18:50:22.430 回答