代码之家  ›  专栏  ›  技术社区  ›  Lambdageek

一个更严格的控制。Monad.Trans.Writer.Strict

  •  12
  • Lambdageek  · 技术社区  · 10 年前

    因此,我们有:

    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 is defined as :

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

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

    我们想要的是尽早履行工会。我们需要更严格的严格要求。作者:

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

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

    1 回复  |  直到 10 年前
        1
  •  16
  •   Gabriella Gonzalez    10 年前

    您的问题的直接答案是:不,没有标准库提供此功能。此外,您提出的版本仍然会泄漏。我知道唯一不泄漏的版本是模拟 WriterT 使用严格的 StateT 我写了一篇非常详细的 e-mail about this to the Haskell libraries mailing list 比较了几种实现的严格性和性能。长话短说:实施 写入器T 作为一个严格的 状态T 不仅消除了空间泄漏,而且生成了非常高效的代码。

    以下是有效的实施:

    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 在某些情况下,但有一些小问题需要解决(例如,模块名称应该是什么)。