重复使用导管是否安全?

时间:2013-11-05 16:25:12

标签: haskell conduit

使用相同的导管值执行多个操作是否安全?像

这样的东西
do
  let sink = sinkSocket sock

  something $$ sink
  somethingElse $$ sink

我记得在导管的早期版本中有一些脏的黑客使得这不安全。目前的状况是什么?

(请注意,sinkSocket不会关闭套接字。)

2 个答案:

答案 0 :(得分:8)

这种用法是完全安全的。旧版本中的问题与模糊可恢复和不可恢复组件之间的界限有关。对于现代版本(我认为自0.4以来),两者之间的界限非常明确。

答案 1 :(得分:1)

在"使用"的语义的意义上重用接收器可能是安全的。水槽没有变化。但你应该意识到另一个威胁:空间泄漏。

这种情况类似于懒惰列表:你可以在一个恒定的空间中懒惰地使用一个巨大的列表,但如果你处理两次列表,它将被保存在内存中。使用递归monadic表达式可能会发生同样的事情:如果你使用它一直是大小,但如果你重复使用它,计算结构将保留在内存中,导致空间泄漏。

以下是一个例子:

import Data.Conduit
import Data.Conduit.List
import Control.Monad.Trans.Class (lift)

consumeN 0 _ = return ()
consumeN n m = do
  await >>= (lift . m)
  consumeN (n-1) m

main = do
  let sink = consumeN 1000000 (\i -> putStrLn ("Got one: " ++ show i))
  sourceList [1..9000000::Int] $$ sink
  sourceList [1..22000000::Int] $$ sink

这个程序在我的机器上使用了大约150M的ram,但是如果你删除了最后一行或者在两个地方都重复了sink的定义,那么你可以得到一个很好的恒定空间使用。

我同意这是一个人为的例子(这是第一个出现在我脑海中的例子),大多数水槽都不太可能发生这种情况。例如,sinkSocket不会发生这种情况。 (为什么这样设计:因为接收器的控制结构并不依赖于它获得的值。这也是它泄漏的原因。)但是,例如,对于这个来源会更常见。 (许多常见的Source表现出这种行为。sourceList将是一个明显的例子,因为它实际上会将源列表保留在内存中。但是,enumFromTo没有什么不同,尽管没有数据可以保留在内存中,只是monadic计算的结构。)

所以,总而言之,我认为了解这一点非常重要。