Question

I have a Conduit of type Conduit a m a and a function of type (a -> Maybe a). I want to run the function, and then if it returns Nothing, use the Conduit. That is, I want a function of type

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

or, of the more restricted type

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

If it helps, my specific case is as follows:

I'm writing code that deals with IRC messages, and I have a function:

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

I want to be able to do maybePipe handlePings pipe (or equivalent) in that function, so when the IRC message is a ping, we respond with a pong and don't call the user-specified Conduit.

Was it helpful?

Solution 2

Using Michael's combinator only calls the (a -> Maybe b) on the first item it comes acress, then lets the onNothing pipe take over. This was not what I was looking for.

Instead, using ZipConduit, in my specific example (using conduit-combinators):

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

or, generalized

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

Unfortunately, this calls the (a -> Maybe b) function two times.

OTHER TIPS

Searching Hoogle reveals a function with almost exactly that type signature: mapOutputMaybe. But the more idiomatic way would be to fuse with Data.Conduit.List.mapMaybe.

EDIT

Scratch that, I understand what you're asking now. No, there's no built in combinator. But it's easy to build one up:

myHelper onNothing f = awaitForever $ maybe onNothing yield . f
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top