将带有ExceptT的源传递到接收器

时间:2016-06-18 18:39:30

标签: haskell conduit

给定具有以下类型签名的管道源:

sourceMsg :: MonadIO m => ExceptT Err (ConduitM () ByteString m) ()

怎样才能传递给Data.Conduit.List.mapM_?下面的内容不起作用,因为输出的类型为ConduitM a o m (),而不是ConduitM a o m (Either Err ())

> :t  ($$) (runExceptT $ sourceMsg undefined) (mapM_ undefined)
<interactive>:1:7: Warning:
    Couldn't match type ‘Either Err ()’ with ‘()’
    Expected type: Source m ByteString
      Actual type: ConduitM () ByteString m (Either Err ())
    In the first argument of ‘($$)’, namely
      ‘(runExceptT $ sourceMsg undefined)’
    In the expression:
      ($$) (runExceptT $ sourceMsg undefined) (mapM_ undefined)

我只想在mapM_内打印bytestring的长度。

1 个答案:

答案 0 :(得分:1)

假设您的汇渠管段为C.mapM_ BS.putStrLn

第一步是打开ExceptT值并将其与接收器段进行比较:

runExceptT sourceMsg :: ConduitM () ByteString IO (Either Err ())
C.mapM_ BS.putStrLn  :: ConduitM ByteString a  IO ()

当您使用Conduit融合运算符时,您必须选择哪个段将返回融合表达式的值。两个主要选择是:

a =$= b                -- b returns the value
a `fuseUpstream` b     -- a returns the value

由于我们希望看到Either Err ()值,我们将使用fuseUpstream

let f = (runExceptT sourceMsg) `fuseUpstream` (C.mapM_ BS.putStrLn)
    :: ConduitM () c IO (Either Err ())

(将两个段融合在一起的另一个要求是,返回值的段必须返回()。在我们的情况下,C.mapM_ ...已满足此要求,但是一般来说,这是要检查的内容。可以始终修改一个细分,以通过使用() fmapping来返回const ()

下一步是运行融合段以产生IO动作:

runConduit f :: IO (Either Err ())

现在我们可以判断是否有错误。完整的解决方案:

{-# LANGUAGE OverloadedStrings #-}

import Control.Monad.Trans.Except
import Data.Conduit as C
import Data.Conduit.List as C
import qualified Data.ByteString.Char8 as BS
import Data.ByteString (ByteString)
import Control.Monad

type Err = (Int, String)

sourceMsg :: ExceptT Err (ConduitM () ByteString IO) ()
sourceMsg = undefined

runSource = do
  do r <- runConduit $ (runExceptT sourceMsg) `fuseUpstream` (C.mapM_ BS.putStrLn)
     case r of
       Left _ -> putStrLn "error"
       Right _ -> putStrLn "no error"