问题 一个更严格的Control.Monad.Trans.Writer.Strict


所以我们有:

import Control.Monad.Writer.Strict

type M a = Writer (Map Key Val) a

对于一些 Key 和 Val

只要我们不查看收集的输出,一切正常:

report comp = do
  let (a,w) = runWriter comp
  putStrLn a

但是,如果我们想要检查 w,我们得到堆栈溢出。

 report comp = do
   let (a,w) = runWriter comp
   guard (not $ null w) $ do -- forcing w causes a stack overflow
     reportOutputs w
   putStrLn a

我认为原因是因为 (>>=) 对于 Writer  被定义为

m >>= k  = WriterT $ do
    (a, w)  <- runWriterT m
    (b, w') <- runWriterT (k a)
    return (b, w `mappend` w')

如果我有一个大的 Writer a 计算,它构建了一长串的mappends: w <> (w' <> (w'' <> ...)) 在这种情况下,这是一个 Map.union 这是严格的地图脊柱。因此,如果我构建了一个大的联合序列,那么只要我强制堆栈溢出的Map就必须对整个事物进行评估。

我们想要的是尽早完成工会。我们想要一个更严格的Strict.Writer:

m >>= k = WriterT $ do
    (a, w) <- runWriterT m
    (b, w') <- runWriterT (k a)
    let w'' = w `mappend` w'
    w'' `seq` return (b, w'')

所以我的问题是:这是否存在于某些“标准”库中?如果没有,为什么不呢?


2937
2017-09-09 15:56


起源

那已经遇到了 管道中的空间泄漏与RWST但是我对“标准库”问题没有答案。 “为什么不”可能过于自以为是。 - Zeta
那么“为什么不”主要是期待一些回应,比如“它不在图书馆,因为你在制定太严格时打破了monad法则”或类似的技术原因。不像“因为[意见]”。 - Lambdageek


答案:


您问题的直接答案是:不,没有标准库提供此功能。此外,您提出的版本仍将泄漏。我所知道的唯一不泄漏的版本是模拟 WriterT 使用严格 StateT。我写的非常详细 通过电子邮件向Haskell图书馆邮件列表发送电子邮件 比较几种实现的严格性和性能。长话短说:实施 WriterT 作为一个严格的 StateT 不仅可以消除空间泄漏,还可以生成非常高效的代码。

这是有效的实现:

newtype WriterT w m a = WriterT { unWriterT :: w -> m (a, w) }

instance (Monad m, Monoid w) => Monad (WriterT w m) where
    return a = WriterT $ \w -> return (a, w)
    m >>= f  = WriterT $ \w -> do
        (a, w') <- unWriterT m w
        unWriterT (f a) w'

runWriterT :: (Monoid w) => WriterT w m a -> m (a, w)
runWriterT m = unWriterT m mempty

tell :: (Monad m, Monoid w) => w -> WriterT w m ()
tell w = WriterT $ \w' ->
    let wt = w `mappend` w'
     in wt `seq` return ((), w `mappend` w')

我想看到这个增加 transformers 在某些时候,但有一些小问题需要解决(即模块名称应该是什么,一个)。


16
2017-09-09 17:19



现在可以作为图书馆使用吗? - user3237465
@ user3237465有 作家CPS-MTL (要么 作家厘泊满,如果你想要一些额外的 镜片 和 mmorph 它的实例)。 - duplode