Haskell Conduit:让Sink根据上游的值返回一个值

时间:2015-03-07 16:29:43

标签: haskell conduit

我一直在尝试使用Conduit库来处理涉及文件的简单I / O,但是我很难过。

我有一个文本文件,只包含几个数字,例如1234。我有一个使用readFile(没有管道)读取文件的函数,并返回Maybe Int(当文件实际上不存在时返回Nothing)。我试图写一个使用导管的这个功能的版本,我只是无法弄明白。

这就是我所拥有的:

import Control.Monad.Trans.Resource
import Data.Conduit
import Data.Functor
import System.Directory
import qualified Data.ByteString.Char8 as B
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.Text as CT
import qualified Data.Text as T

myFile :: FilePath
myFile = "numberFile"

withoutConduit :: IO (Maybe Int)
withoutConduit = do
    doesExist <- doesFileExist myFile
    if doesExist
      then Just . read <$> readFile myFile
      else return Nothing

withConduit :: IO (Maybe Int)
withConduit = do
    doesExist <- doesFileExist myFile
    if doesExist
      then runResourceT $ source $$ conduit =$ sink
      else return Nothing
  where
    source :: Source (ResourceT IO) B.ByteString
    source = CB.sourceFile myFile

    conduit :: Conduit B.ByteString (ResourceT IO) T.Text
    conduit = CT.decodeUtf8

    sink :: Sink T.Text (ResourceT IO) (Maybe Int)
    sink = awaitForever $ \txt -> let num = read . T.unpack $ txt :: Int
                                  in -- I don't know what to do here...

有人可以帮我完成sink功能吗? 谢谢!

1 个答案:

答案 0 :(得分:3)

这对于管道实际上提供很多价值的地方来说并不是一个很好的例子,至少不是你现在正在看它的方式。具体来说,您正在尝试使用read函数,该函数要求整个值都在内存中。此外,您当前的错误处理行为有点松散。从本质上讲,如果内容中出现任何意外情况,您只会收到read: no parse错误。

然而, 是一种我们可以在管道中使用它并且有意义的方式:通过自己解析ByteString逐字节并避免read函数。幸运的是,这种模式属于标准的左折叠,导管 - 组合器包为(导管中的元素左折叠,又名foldlCE)提供了完美的功能:

{-# LANGUAGE OverloadedStrings #-}
import Conduit
import Data.Word8
import qualified Data.ByteString as S

sinkInt :: Monad m => Consumer S.ByteString m Int
sinkInt =
    foldlCE go 0
  where
    go total w
        | _0 <= w && w <= _9 =
            total * 10 + (fromIntegral $ w - _0)
        | otherwise = error $ "Invalid byte: " ++ show w

main :: IO ()
main = do
    x <- yieldMany ["1234", "5678"] $$ sinkInt
    print x

有很多注意事项:如果有意外的字节,它只会抛出一个异常,并且它根本不处理整数溢出(虽然修复 只是一个用Int替换Integer的问题。重要的是要注意,因为有效的32位或64位int的内存中字符串表示总是很小,导管对于这个问题是过度的,尽管我希望这段代码提供一些如何一般的指导写管道代码。