如何使用管道组合器实现类似takeWhile的功能?



我正在尝试实现一个结合了groupBytakeWhile思想的函数,并在内部使用后者。具体来说,它将连续返回当前谓词True的所有元素分组为列表,然后它将继续对下一个谓词执行相同的操作,依此类推:

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识别?(;这是由于缺乏使用一些语言扩展,还是有其他问题?

• 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确实会使错误消失,但我会注意到,在组合器库中,这似乎是不必要的。

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

• 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,我目前只剩下:

• 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 问题需要修复,但至少仍然存在一个:

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

• 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 ())来帮助的,我有一些可以编译的东西,但仍然需要测试:

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

我得出的最终结论是,如果我想实现这个组件,我不能在组件组合(融合(方面做到这一点。

这是一个似乎有效的实现:

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

这个设计受到我所知道的最相似的函数(虽然来自一个不推荐使用的库(groupBy的影响。

最新更新