我正在尝试做这样的事情:
processListIO :: [A] -> IO [B]
processListIO xs = bracket ini fin $ \s -> mapM (upd s) xs
where
ini :: IO (Ptr S)
upd :: Ptr S -> A -> IO B
fin :: Ptr S -> IO ()
基本上,这是一个迭代列表的计算,将每个元素映射到其他元素,并在过程中使用内部私有状态。具体的ini
, upd
, 和fin
我想到的来自 C 库,但它们保证“表现良好”,因为它们只是分配新状态,执行唯一副作用是修改状态的计算,然后解除分配状态. 我相信,这意味着我可以安全地放在unsafePerformIO
前面并获得一个纯函数:
processList :: [A] -> [B]
processList = unsafePerformIO . processListIO
现在我想做同样的事情,但使用conduit
(或者,实际上,任何其他流媒体库)。但是,由于计算本质上是无效的,我希望我的管道是纯的:
processStream :: ConduitT A B Identity ()
甚至更好:
processStream :: forall m. Monad m => ConduitT A B m ()
(我怀疑后者可能没有意义,因为看起来这个技巧对简单列表很有效,只是因为元素是纯的。)
理想情况下,我想对用户完全隐藏计算需要一个状态并进行外部调用的事实,并且只是假装它只是类似于 a scanl
(或者mapAccum
,正如conduit
它所称的那样)。
这可能吗?如何使用conduit
(或其他流媒体库)执行此操作?