haskell 使用另一导管部分处理导管

kokeuurv  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(88)

我正在寻找一个函数与以下签名(我认为):

partialProcessConduit :: forall m a b r. Monad m 
  => (a -> (b, Maybe (ConduitT () a m ()) )) 
  -> ConduitT b Void m r 
  -> ConduitT () a m () 
  -> m (r, ConduitT () a m ())
partialProcessConduit splitFunc consumingConduit sourceConduit

字符串
它基本上做了以下事情:
1.从管道sourceConduit中重复获取a类型的值。
1.将函数splitFunc转换为a
1.将值bsplitFunc推入consumingConduit
1.如果splitFunc返回Just (some conduit)(即不是Nothing),则返回对的第二部分
1.“close up”consumingConduit,得到结果值r
1.返回带有sourceConduit的“rest”的导管,但导管位于其前面的Just中。
我实际上已经实现了一些接近这一点的东西(提前为蹩脚的命名道歉)。

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Conduit (ConduitT, SealedConduitT, unsealConduitT, ($$+), await)
import Data.Void (Void)
import qualified Control.Arrow as Arrow
import Data.DList (DList)

partialProcessConduitInMemory :: forall m a b r. Monad m 
  => (a -> (b, Maybe (ConduitT () a m ()))) 
  -> (DList b -> r) 
  -> ConduitT () a m () 
  -> m (r, ConduitT () a m ())
partialProcessConduitInMemory splitFunc collapseDList sourceConduit = do
  (sc :: SealedConduitT () a m (), (result :: r, leftOver :: ConduitT () a m ())) <- x
  pure (result, leftOver >> unsealConduitT sc)
  where
    x :: m (SealedConduitT () a m (), (r, ConduitT () a m ()))
    x = sourceConduit $$+ g
    g :: ConduitT a Void m (r, ConduitT () a m ())
    g = Arrow.first collapseDList <$> go mempty
    go :: DList b -> ConduitT a Void m (DList b, ConduitT () a m ())
    go blockList = await >>= \case
      Nothing -> pure (blockList, pure ())
      Just block -> case splitFunc block of
        (transformedBlock, Nothing) -> go $ blockList <> pure transformedBlock
        (transformedBlock, Just leftOver) -> pure (blockList <> pure transformedBlock, leftOver)


这几乎是我想要的。注意这里的类型签名与上面的相同,除了第二个参数。这里,不是传递一个使用元素作为第二个参数的管道接收器,而是在一个DList中收集它们。我宁愿使用管道接收器来使用管道源的第一部分,而不是收集列表中的所有元素并处理它们。 我可以在这里使用管道接收器而不是DList吗?如果可以,我需要做什么样的调整?我想在go循环中将元素推入接收器中,而不是仅仅追加它们,然后执行runConduit以获得结果r`,但我无法很好地处理类型。任何帮助都很感激。

crcmnpdw

crcmnpdw1#

我猜你想要这样的东西:

{-# LANGUAGE ScopedTypeVariables #-}

partialProcessConduit :: forall m a b r. Monad m
  => (a -> (b, Maybe (ConduitT () a m ()) ))
  -> ConduitT b Void m r
  -> ConduitT () a m ()
  -> m (r, ConduitT () a m ())
partialProcessConduit f snk src = do
  (rest2, (mrest1,r)) <- src $$+ fuseBoth loop snk
  pure (r, maybe id (>>) mrest1 (unsealConduitT rest2))
  where loop :: ConduitT a b m (Maybe (ConduitT () a m ()))
        loop = do ma <- await
                  case ma of
                    Just a -> do
                      let (b, mrest) = f a
                      yield b
                      case mrest of
                        Nothing -> loop
                        Just rest -> pure (Just rest)
                    Nothing -> pure Nothing

字符串
这里的loop管道的类型为ConduitT a b m (Maybe (ConduitT () a m ()),因此它输入a s并输出b s,直到f(也称为splitFunc)返回前缀管道,在这种情况下,它返回Just管道。如果splitFunc从不返回管道,则返回Nothing
现在,我们可以使用fuseBoth loop snk,它的类型为ConduitT a Void m (Maybe (ConduitT () a m (), r)。这将b s从loop接收到snk,返回来自splitFunc的前缀管道(如果有的话)和来自snk的返回值r
最后,我们可以src $$+ fuseBoth loop snk。这将运行整个管道,从src中提取a s,并将b s下沉到snk中,直到splitFunc返回前缀管道,此时它将返回:

(SealedConduitT () a m (), (Maybe (ConduitT () a m ()), r))


令人难以置信的是,密封管道是src的剩余部分,Maybe管道是splitFunc返回的“前缀”管道,最后的rsnk的返回值。剩下的就是将它们粘合在一起成为适当的返回值。
这似乎是按照以下测试工作:

main :: IO ()
main = do
  (r, c) <- partialProcessConduit foo (printC >> pure 999) (yieldMany [1,2,3,4,7,8,9])
  runConduit (c .| printC)
  print r

  where foo 4 = (42, Just (yieldMany [5,6]))
        foo n = (10*n, Nothing)


这将产生:

λ> main
10
20
30
42
5
6
7
8
9
999


看起来是对的

相关问题