如何使用Conduit组合器实现类似takeWhile的功能? [英] How to implement a takeWhile-like function using Conduit combinators?

查看:51
本文介绍了如何使用Conduit组合器实现类似takeWhile的功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现一个功能,该功能结合了 groupBy takeWhile 的思想,并在内部使用后者.具体来说,它将所有在当前谓词上连续返回 True 的元素归为一个列表,然后对下一个谓词进行相同的操作,以此类推:

I'm trying to implement a function that combines the ideas of groupBy and takeWhile, as well as uses the latter internally. Specifically, it will group all elements that consecutively return True on the current predicate as a list, then it will proceed to do the same with the next predicate, and so on:

   takeWhileGrouped :: (Monad m, MonoFoldable mono) =>
      ([Element mono -> Bool]) -> ConduitT (Element mono) [Element mono] m ()
    takeWhileGrouped preds = go preds
      where
        go (pred:nextPreds) = yield (goIter pred) >> go nextPreds
        goIter pred = takeWhile pred .| sinkList

此实现很可能会遇到其他问题,但是在此阶段,我收到一个编译错误,不确定如何继续(为什么 mono0 无法用 mono ?);是因为缺少使用某种语言扩展名还是其他问题?

It's entirely likely this implementation suffers other issues, but at this stage I'm getting a compilation error that I'm not sure how to proceed with (why can't mono0 be identified with mono?); is this due to the lack of using some language extension, or is there another issue?

    • Couldn't match type ‘Element mono0’ with ‘Element mono’
      Expected type: [Element mono -> Bool]
                     -> ConduitT (Element mono) [Element mono] m ()
        Actual type: [Element mono0 -> Bool]
                     -> ConduitT (Element mono0) [Element mono0] m ()
      NB: ‘Element’ is a non-injective type family
      The type variable ‘mono0’ is ambiguous
    • In the ambiguity check for ‘takeWhileGrouped’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      In the type signature:
        takeWhileGrouped :: (Monad m, MonoFoldable mono) =>
                            ([Element mono -> Bool])
                            -> ConduitT (Element mono) [Element mono] m ()
    |
140 | takeWhileGrouped :: (Monad m, MonoFoldable mono) =>
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

更新1

启用 AllowAmbiguousTypes 确实会使错误消失,但是我会注意到在

Enabling AllowAmbiguousTypes does make the error go away, but I'll note that in the combinators library this appears to be unnecessary.

现在真正的问题显现出来了:

Now the real issues show themselves:

    • Couldn't match type ‘ConduitT a0 c0 m0 [b0]’
                     with ‘[Element mono]’
      Expected type: ConduitT (Element mono) [Element mono] m ()
        Actual type: ConduitT
                       (Element seq0) (ConduitM a0 c0 m0 [b0]) m ()
    • In the expression: go preds
      In an equation for ‘takeWhileGrouped’:
          takeWhileGrouped preds
            = go preds
            where
                go (pred : nextPreds) = yield (goIter pred) >> go nextPreds
                goIter pred = takeWhile pred .| sinkList
    • Relevant bindings include
        preds :: [Element mono -> Bool]
          (bound at src/FDS/Data/Conduits.hs:143:18)
        takeWhileGrouped :: [Element mono -> Bool]
                            -> ConduitT (Element mono) [Element mono] m ()
          (bound at src/FDS/Data/Conduits.hs:143:1)
    |
143 | takeWhileGrouped preds = go preds
    |                          ^^^^^^^^


    • Couldn't match type ‘seq -> seq’ with ‘ConduitT a b m1 ()’
      Expected type: ConduitM a b m1 ()
        Actual type: seq -> seq
    • Probable cause: ‘takeWhile’ is applied to too few arguments
      In the first argument of ‘(.|)’, namely ‘takeWhile pred’
      In the expression: takeWhile pred .| sinkList
      In an equation for ‘goIter’:
          goIter pred = takeWhile pred .| sinkList
    • Relevant bindings include
        pred :: Element seq -> Bool
          (bound at src/FDS/Data/Conduits.hs:146:12)
        goIter :: (Element seq -> Bool) -> ConduitM a c m1 [b]
          (bound at src/FDS/Data/Conduits.hs:146:5)
    |
146 |     goIter pred = takeWhile pred .| sinkList
    |                   ^^^^^^^^^^^^^^

更新2

我使用了错误的 takeWhile ,现在使用了来自Conduit Cominators的 CC.takeWhile ,我目前的位置是:

I was using the wrong takeWhile, now using CC.takeWhile from Conduit Cominators, I'm currently left with:

    • Couldn't match type ‘ConduitT
                             (Element mono) c0 m0 [Element mono]’
                     with ‘[Element mono]’
      Expected type: ConduitT (Element mono) [Element mono] m ()
        Actual type: ConduitT
                       (Element mono) (ConduitM (Element mono) c0 m0 [Element mono]) m ()
    • In the expression: go preds
      In an equation for ‘takeWhileGrouped’:
          takeWhileGrouped preds
            = go preds
            where
                go (pred : nextPreds) = yield (goIter pred) >> go nextPreds
                goIter pred = CM.takeWhile pred .| sinkList
    • Relevant bindings include
        preds :: [Element mono -> Bool]
          (bound at src/FDS/Data/Conduits.hs:144:18)
        takeWhileGrouped :: [Element mono -> Bool]
                            -> ConduitT (Element mono) [Element mono] m ()
          (bound at src/FDS/Data/Conduits.hs:144:1)
    |
144 | takeWhileGrouped preds = go preds
    |                          ^^^^^^^^


更新3

需要解决一些组合程序API问题,但至少还有一个问题:

Had a few combinator API issues to fix, but at least one still remains:

takeWhileGrouped :: forall m mono. (Monad m, MonoFoldable mono) =>
  ([Element mono -> Bool]) -> ConduitT (Element mono) [Element mono] m ()
takeWhileGrouped preds = go preds
  where
    go (pred:nextPreds) = yieldM (goIter pred) >> go nextPreds
    go [] = yield []
    goIter :: (Element mono -> Bool) -> m ([Element mono])
    goIter pred = (CC.takeWhile pred) .| sinkList & runConduitRes

出乎意料的是,我在 takeWhile 的输入中弹出一个():

Unexpectedly, I have a () popping up in the input to takeWhile:

    • Couldn't match type ‘Element mono’ with ‘()’
      Expected type: () -> Bool
        Actual type: Element mono -> Bool
    • In the first argument of ‘CC.takeWhile’, namely ‘pred’
      In the first argument of ‘(.|)’, namely ‘(CC.takeWhile pred)’
      In the first argument of ‘(&)’, namely
        ‘(CC.takeWhile pred) .| sinkList’
    • Relevant bindings include
        pred :: Element mono -> Bool
          (bound at src/FDS/Data/Conduits.hs:148:12)
        goIter :: (Element mono -> Bool) -> m [Element mono]
          (bound at src/FDS/Data/Conduits.hs:148:5)
        preds :: [Element mono -> Bool]
          (bound at src/FDS/Data/Conduits.hs:144:18)
        takeWhileGrouped :: [Element mono -> Bool]
                            -> ConduitT (Element mono) [Element mono] m ()
          (bound at src/FDS/Data/Conduits.hs:144:1)
    |
148 |     goIter pred = (CC.takeWhile pred) .| sinkList & runConduitRes
    |                                 ^^^^

更新4

修复了更多的逻辑和类型错误后,通过添加内部类型注释(CC.takeWhile curPred :: ConduitT(Element mono)(Element mono)m())确实有帮助,我有一些可以编译的东西,但是仍然需要测试:

After fixing some more logic and type errors, which was really aided by adding an internal type annotation (CC.takeWhile curPred :: ConduitT (Element mono) (Element mono) m ()), I have something that compiles, but still needs testing:

takeWhileGrouped :: forall m mono. (Monad m, MonoFoldable mono) =>
  ([Element mono -> Bool])
  -> ConduitT () (Element mono) m ()
  -> ConduitT (Element mono) [Element mono] m ()
takeWhileGrouped preds conIn = go preds
  where
    go (curPred:nextPreds) = yieldM (goIter curPred) >> go nextPreds
    go [] = yield []
    goIter :: (Element mono -> Bool) -> m ([Element mono])
    goIter curPred = conIn .| CC.takeWhile curPred .| sinkList & runConduit

推荐答案

我最终得出的结论是,如果我想实现此组件,则

I came to the eventual conclusion that, if I wanted to implement this component, I couldn't do it in terms of component composition (fusion).

这似乎是可行的实现:

takeWhileGrouped :: forall m mono. Monad m =>
  [Element mono -> Bool] -> ConduitT (Element mono) [Element mono] m ()
takeWhileGrouped preds = start
  where
    start = await >>= maybe (return ()) (loop preds [])
    loop :: [(Element mono) -> Bool] -> [Element mono] -> (Element mono)
      -> ConduitT (Element mono) [Element mono] m ()
    loop [] _ _ = yield []
    loop curPreds@(predF:predRest) accum x =
        await >>= maybe (yield accumX) go
      where
        accumX = if predF x then x:accum else accum
        go y = if predF y then loop curPreds accumX y
               else yield accumX >> loop predRest [] y

此设计受我所知(尽管已弃用的库)最相似的功能的影响,

This design was influenced by the most similar function I knew of (though from a deprecated library), groupBy.

这篇关于如何使用Conduit组合器实现类似takeWhile的功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆