一个简单的例子表明IO不能满足monad法则?
我已经提到IO
不符合单子法,但是我没有find一个简单的例子来表明这一点。 有人知道一个例子吗? 谢谢。
编辑:正如ertes和nm指出的,使用seq
有点不合法,因为它可以使任何monad失败的法律(结合undefined
)。 由于undefined
可能被视为非终止计算,所以使用它是完全正确的。
所以修改后的问题是: 有人知道一个例子,说明IO
不能满足单子法则,不使用seq
? (或者,如果seq
不被允许,那么certificateIO
确实符合法律?)
tl; dr先行: seq
是唯一的方法。
由于IO
的实现不是标准规定的,我们只能看具体的实现。 如果我们看一下GHC的实现,就像从源头上得到的那样(可能IO
一些幕后特殊处理引入了违反单子法的行为,但是我不知道这种情况),
-- in GHC.Types (ghc-prim) newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #)) -- in GHC.Base (base) instance Monad IO where {-# INLINE return #-} {-# INLINE (>>) #-} {-# INLINE (>>=) #-} m >> k = m >>= \ _ -> k return = returnIO (>>=) = bindIO fail s = failIO s returnIO :: a -> IO a returnIO x = IO $ \ s -> (# s, x #) bindIO :: IO a -> (a -> IO b) -> IO b bindIO (IO m) k = IO $ \ s -> case ms of (# new_s, a #) -> unIO (ka) new_s thenIO :: IO a -> IO b -> IO b thenIO (IO m) k = IO $ \ s -> case ms of (# new_s, _ #) -> unIO k new_s unIO :: IO a -> (State# RealWorld -> (# State# RealWorld, a #)) unIO (IO a) = a
它被实现为(严格)状态monad。 所以任何违反IO
的monad法则,都是由Control.Monad.State[.Strict]
。
让我们看看单子法,看看IO
发生了什么:
return x >>= f ≡ fx: return x >>= f = IO $ \s -> case (\t -> (# t, x #)) s of (# new_s, a #) -> unIO (fa) new_s = IO $ \s -> case (# s, x #) of (# new_s, a #) -> unIO (fa) new_s = IO $ \s -> unIO (fx) s
忽略newtype包装,这意味着return x >>= f
变成\s -> (fx) s
。 (可能)与fx
区分的唯一方法是seq
。 (如果fx ≡ undefined
, seq
只能区分它。)
m >>= return ≡ m: (IO k) >>= return = IO $ \s -> case ks of (# new_s, a #) -> unIO (return a) new_s = IO $ \s -> case ks of (# new_s, a #) -> (\t -> (# t, a #)) new_s = IO $ \s -> case ks of (# new_s, a #) -> (# new_s, a #) = IO $ \s -> ks
再次忽略newtype包装, k
被replace为\s -> ks
,它只能被seq
区分,并且只有在k ≡ undefined
。
m >>= (\x -> gx >>= h) ≡ (m >>= g) >>= h: (IO k) >>= (\x -> gx >>= h) = IO $ \s -> case ks of (# new_s, a #) -> unIO ((\x -> gx >>= h) a) new_s = IO $ \s -> case ks of (# new_s, a #) -> unIO (ga >>= h) new_s = IO $ \s -> case ks of (# new_s, a #) -> (\t -> case unIO (ga) t of (# new_t, b #) -> unIO (hb) new_t) new_s = IO $ \s -> case ks of (# new_s, a #) -> case unIO (ga) new_s of (# new_t, b #) -> unIO (hb) new_t ((IO k) >>= g) >>= h = IO $ \s -> case (\t -> case kt of (# new_s, a #) -> unIO (ga) new_s) s of (# new_t, b #) -> unIO (hb) new_t = IO $ \s -> case (case ks of (# new_s, a #) -> unIO (ga) new_s) of (# new_t, b #) -> unIO (hb) new_t
现在,我们一般都有
case (case e of case e of pat1 -> ex1) of ≡ pat1 -> case ex1 of pat2 -> ex2 pat2 -> ex2
根据公式3.17.3(a)中的语言报告,所以这个法则不仅保持模数。
总结一下, IO
满足monad定律,除了seq
可以区分undefined
和\s -> undefined s
。 这同样适用于State[T]
, Reader[T]
, (->) a
和包装函数types的其他monad。
Haskell中的所有monad都是monad,如果你排除怪异的seq
组合。 IO
也是如此。 由于seq
实际上并不是一个普通的函数,而是涉及到魔法,所以你必须排除它来检查单子法则,因为你必须排除unsafePerformIO
。 使用seq
你可以certificate所有monad错误,如下所示。
在克莱斯里范畴中,单子产生, return
是身份态射,而(<=<)
是组合。 所以return
必须是(<=<)
的标识:
return <=< x = x
甚至使用seq
身份和也许不成为单子:
seq (return <=< undefined :: a -> Identity b) () = () seq (undefined :: a -> Identity b) () = undefined seq (return <=< undefined :: a -> Maybe b) () = () seq (undefined :: a -> Maybe b) () = undefined
单子法之一是这样的
m >>= return ≡ m
让我们试试GHCi:
Prelude> seq ( undefined >>= return :: IO () ) "hello, world" "hello, world" Prelude> seq ( undefined :: IO () ) "hello, world" *** Exception: Prelude.undefined
所以undefined >>= return
与undefined
不一样,所以IO
不是monad。 如果我们为Maybe
monad尝试同样的事情,另一方面:
Prelude> seq ( undefined >>= return :: Maybe () ) "hello, world" *** Exception: Prelude.undefined Prelude> seq ( undefined :: Maybe () ) "hello, world" *** Exception: Prelude.undefined
这两个expression式是相同的 – 所以这个例子Maybe
并不排除是一个monad。
如果有人有一个不依赖于seq
的使用或undefined
的例子,我会有兴趣看到它。
m >>= return ≡ m
被打破:
sequence_ $ take 100000 $ iterate (>>=return) (return ()) :: IO ()
杂乱的记忆和增加计算时间
sequence_ $ take 100000 $ iterate (>>=return) (return ()) :: Maybe ()
才不是。
AFAIR有一个Monad Transformer解决了这个问题。 如果我猜对了,那就是Codensity Monad Transformer。