给定具有以下类型签名的管道源:
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的长度。
答案 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"