Mor*_*orJ 5 monads haskell lazy-evaluation
我在编写代码时Control.Monad.Writer.Lazy使用的(,) [String]是我的编写器monad。但我发现,(>>=)和(>>)过于严格与幺操作?它们使用以下代码导致无限循环:
type Wrtr a = ([String], a)
writer (x, w) = (w, x)
main :: IO ()
main = do
let one = writer ((), ["goodbye"])
let w = foldr1 (>>) $ repeat one
let (log, _) = w
mapM_ putStrLn . take 5 $ log
Run Code Online (Sandbox Code Playgroud)
这段代码将无限循环,并且永远不会打印任何内容,这对我来说是不好的。所以现在我正在使用同一monad的这个天真的实现,这似乎很好并且很懒:
data Writer w a = Writer w a
instance Functor (Writer w) where
fmap f (Writer w x) = Writer w (f x)
instance Monoid w => Applicative (Writer w) where
pure x = Writer mempty x
(Writer w1 f) <*> (Writer w2 x) = Writer (w1 <> w2) (f x)
instance Monoid w => Monad (Writer w) where
return = pure
(Writer w1 x) >>= f =
let (Writer w2 y) = f x
in Writer (w1 <> w2) y
writer (x, w) = Writer w x
Run Code Online (Sandbox Code Playgroud)
(由于monoid约束,您必须定义函子和应用实例)
如果然后使用与main上面相同的功能运行代码,它将打印5次“再见”并退出。
所以问题是:为什么元组这么严格?或者,如果不严格,它是什么?为什么在那里?
顺便说一句,我使用的是ghc 8.6.4,而其他所有东西都带有堆栈lts-13.19
那是因为您Writer违反了monad法律。看这条法则:
-- forall (f :: a -> Writer m b) (x :: a).
return x >>= f = f x
-- basically f (id x) = f x, which we should agree is pretty important!
Run Code Online (Sandbox Code Playgroud)
但是,a,这不成立!let f = undefined!(或者f = const undefined;如果您不使用它们,则无法区分它们seq)
return x >>= undefined
= Writer mempty x >>= undefined
= let (Writer m y) = undefined
in Writer (mempty <> m) y
= Writer (mempty <> undefined) undefined
Run Code Online (Sandbox Code Playgroud)
但是,根据法律,
return x >>= undefined
= undefined x
= undefined
Run Code Online (Sandbox Code Playgroud)
这些不相等,因此您的懒惰Monad实例是非法的(是的,我相信in也是这样mtl)。但是,快速松散推理在道德上是正确的,因此我们通常只接受它。这个想法是,Writer只要您保持无限或最低的值,一个懒惰的monad通常会遵循其应遵循的规律,但是在那些极端情况下它会崩溃。相反,严格执行是完全合法的,这就是为什么要这样做base。但是,正如您所发现的,当lazy Writer违反法律时,他们会以一种有用的方式来执行此操作,因此我们将lazy实现放置在中mtl。
这是此行为的演示。请注意,惰性版本Writer "在爆炸前会在输出中生成a ,而严格版本和法律给出的规范都不会这样做。