有一个惰性/严格的Writer版本有什么意义?
为什么Haskell中有两种不同的Writer类型monad? 直观地看,读“严格的作家monad”意味着<>
严格,所以日志中没有thunk积累。 但是,从源代码看,情况并非如此:
-- Lazy Writer
instance (Monoid w, Monad m) => Monad (WriterT w m) where
-- ...
m >>= k = WriterT $ do
~(a, w) <- runWriterT m
~(b, w') <- runWriterT (k a)
return (b, w <> w')
在严格的版本中,模式不是无可辩驳的,即~
缺失。 所以上面发生的是m
和ka
不被评估,而是以thunk存储。 在严格版本中,评估它们以检查它们是否匹配元组模式,结果被馈送到<>
。 在这两种情况下, >>=
都不会被评估,除非实际需要结果值。 所以,我理解它的方式是,无论是懒惰和严格的版本做同样的事情,不同的是他们在定义内的不同位置形实转换>>=
:懒产生runWriterT
的thunk,严格的生产<>
的thunk。
这让我有两个问题:
<>
而无需编写自己的包装和实例? 你的第一个观察是正确的,但是这个Thunk创建的区别很重要。
Lazy
和Strict
不是关于日志类型的严格性,而是关于对的严格性。
这是因为Haskell中的一对有两种可能的方式来更新它。
bimap f g (a,b) = (f a, g b)
要么
bimap f g ~(a,b) = (f a, g b)
后者与之相同
bimap f g p = (f (fst p), g (snd p))
这两者之间的区别在于,如果您在第一种情况下将参数传递给bimap
,则会立即强制该对。
在后一种情况下,这一对并不是立即强制的,但我反而给你一个(,)
回填两个非严格的计算。
这意味着
fmap f _|_ = _|_
在第一种情况下,但
fmap f _|_ = (_|_, _|_)
在第二副懒惰的一对情况下!
在对一对概念的不同解释下,两者都是正确的。 一种是假装一对强迫你是在绝对意义上的对,它没有任何有趣_|_
的在自己的权利。 另一方面,该领域的解释是非严格的。 尽可能让所有尽可能多的程序终止,让你进入Lazy
版本。
(,) e
是一个完全可以接受的Writer
,所以这就是问题的特征。
区分的原因在于,重要的是终止许多通过monad获得固定点的外来程序。 只要他们懒惰,你就可以回答关于某些涉及州或作家的循环计划的问题。
请注意,在这两种情况下,'log'参数都不是严格的。 一旦发生严重错误,你会失去适当的关联性,并在技术上停止成为Monad
。 = /
因为这不是一个monad,我们不提供它在mtl
!
有了这个,我们可以解决你的第二个问题:
虽然有一些解决方法。 你可以建立一个假的Writer
之上State
。 基本上假装你不是一个国家的论点。 和刚刚mappend进入状态,你会tell
。 现在你可以严格执行此操作,因为它不会发生在你背后,作为每一次绑定的一部分。 State
只是在行动之间通过未经修改的国家。
shout :: Monoid s => s -> Strict.StateT s m ()
shout s' = do
s <- get
put $! s <> s'
然而,这意味着你迫使你的整个State
monad获得输出,并且Monoid
产生Monoid
一部分,但是你得到的东西在操作上更接近严格的程序员所期望的。 有趣的是这个工程即使只有Semigroup
,因为只有使用mempty
在开始有效的是,当你runState
。
上一篇: What is the point of having a lazy/strict version of Writer?
下一篇: strict vs non