如何使用管道为嵌套流建模?

时间:2015-10-05 20:22:34

标签: haskell conduit

让我们说我们需要总结存储在文件中的数字组,其中组由空行分隔。所以

1
2
3

4
5

结果是6 9

使用嵌套管道对此进行建模似乎很自然:外部管道会将线条分成线组,其中每个组本身都是源。

但是,我不会直接在导管中看到这种风格。用导管做这个最惯用的方法是什么?

1 个答案:

答案 0 :(得分:2)

我建议有两种基本方法。简单的一个就是产生一个总和类型流,表示“另一个值”与“换行符”。另一种方法是使用组合方法,如withLinefoldLines。后者可以建立在前者之上。最初讨论了这种方法in a blog post on foldLines

下面是一个完整的代码片段,显示了总和类型和组合方法。请注意,折叠(可能是一个坏名称)是一个通用函数,用于从sum类型转换为“嵌套流”。管道(基于FreeT IIRC)采用的方法也可能被管道使用,但正如我在博客文章中提到的,我认为这是一个比需要更复杂的解决方案。

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
import ClassyPrelude.Conduit
import Data.Text.Read (decimal, signed)

sumType :: Monad m => Conduit Text m (Maybe Int)
sumType =
    linesUnboundedC =$= mapMC toMaybeInt
  where
    toMaybeInt "" = return Nothing
    toMaybeInt t =
        case signed decimal t of
            Right (i, "") -> return $ Just i
            _ -> fail $ "Invalid int: " ++ show t

folded :: Monad m => Sink a m () -> Sink (Maybe a) m ()
folded perGroup =
    startGroup
  where
    startGroup = peekC >>= maybe (return ()) (const go)

    go = takeMaybes =$ (perGroup >> sinkNull) >> startGroup

    takeMaybes = await >>= maybe (return ()) (\x ->
        case x of
            Nothing -> return ()
            Just y -> yield y >> takeMaybes)

main :: IO ()
main = do
    let src = yield "1\n2\n3\n\n5\n6\n\n\n7"
    src $$ sumType =$ (sinkList >>= print)
    src $$ sumType =$ folded (sinkList >>= print)