我怎样才能有条件地使用导管?

时间:2014-06-05 01:19:24

标签: haskell conduit

我有一个Conduit a m a类型的管道和一个(a -> Maybe a)类型的功能。我想运行该函数,然后如果它返回Nothing,请使用Conduit。也就是说,我想要一个类型为

的函数
maybePipe :: Conduit a m b -> (a -> Maybe b) -> Conduit a m b

或更受限制的类型

maybePipe :: Conduit a m a -> (a -> Maybe a) -> Conduit a m a

如果有帮助,我的具体情况如下:

我正在编写处理IRC消息的代码,我有一个函数:

runClient :: Conduit IRC.Message IO IRC.Message -> ClientSettings -> IO ()
runClient pipe address = runTCPClient' pipe' address where
    pipe' = mapC IRC.decode $= concatMapC id $= pipe $= mapC IRC.encode $= mapC (++ "\r\n")
    handlePings (IRC.Message (Just (IRC.Server serverName)) "PING" []) = Just $ IRC.pong serverName
    handlePings (IRC.Message Nothing "PING" [server]) = Just $ IRC.pong server
    handlePings (IRC.Message Nothing "PING" []) = Just $ IRC.pong (getHost address)
    handlePings _ = Nothing
    runTCPClient' :: Conduit ByteString IO ByteString -> ClientSettings -> IO ()
    runTCPClient' pipe address = runTCPClient address runClient where
        runClient appdata = appSource appdata $= linesUnboundedAsciiC $= pipe $$ appSink appdata

我希望能够在该函数中执行maybePipe handlePings pipe(或等效),因此当IRC消息是ping时,我们使用pong进行响应,而不是调用用户指定的Conduit。

2 个答案:

答案 0 :(得分:2)

Searching Hoogle会显示一个几乎完全符合该类型签名的函数:mapOutputMaybe。但更惯用的方式是融合Data.Conduit.List.mapMaybe

修改

抓一点,我明白你现在在问什么。不,没有内置的组合器。但建立一个很容易:

myHelper onNothing f = awaitForever $ maybe onNothing yield . f

答案 1 :(得分:0)

使用Michael的组合子只调用它来的第一个项目(a -> Maybe b),然后让onNothing管道接管。这不是我想要的。

相反,在我的具体示例中使用ZipConduit(使用conduit-combinators):

pingHandlingPipe = 
  getZipConduit $ ZipConduit (concatMapC handlePings) 
               *> ZipConduit (takeWhileC (not.isJust.handlePings) $= pipe)

或,概括

pipeMaybe maybeF pipe = 
  getZipConduit $ ZipConduit (concatMapC maybeF)
               *> ZipConduit (takeWhileC (not.isJust.maybeF) $= pipe)

不幸的是,这会两次调用(a -> Maybe b)函数。