我最近读到了Writer
/ WriterT
monad 的空间泄漏问题。如果我正确理解了这个问题,那是因为绑定运算符,即(>>=)
不是尾递归:
m >>= f = WriterT $ do
(a, w1) <- runWriterT m
(b, w2) <- runWriterT (f a)
return (b, w1 `mappend` w2)
定义WriterT
和Writer
:
newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }
type Writer w = WriterT w Identity
我很好奇在第二个参数上引入惰性是否mappend
会解决这个空间泄漏问题。
通过引入惰性,我的意思是类似于(++)
运算符:
(++) :: [a] -> [a] -> [a]
(++) [] ys = ys
(++) (x:xs) ys = x : (xs ++ ys)
结果是在没有实际接触第二个参数的情况下产生的。
现在,如果我们使用m
带有惰性单子绑定的单子(例如m ~ Identity
,它给了我们普通的旧Writer
单子),并使用mappend
上面提到的,那么f a
部分 ( w2
) 在评估时可以保持 thunk mappend w1 w2
,因此结果可以部分消耗 ( w1
) 而没有实际上强制其余表达式(w2
)。
我对此是否正确?Writer
在这样的monad中是否避免了空间泄漏?