Как я могу условно применить проводник?

У меня есть Conduit типа Conduit a m a и функция типа (a -> Maybe a). Я хочу запустить функцию, а затем, если она ничего не возвращает, использовать 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.


person fread2281    schedule 05.06.2014    source источник


Ответы (2)


Поиск в Hoogle обнаруживает функцию с почти точно такой же сигнатурой: mapOutputMaybe. Но более идиоматичным способом было бы слияние с Data.Conduit.List.mapMaybe.

ИЗМЕНИТЬ

Сотрите это, я понимаю, о чем вы сейчас спрашиваете. Нет, встроенного комбинатора нет. Но его легко построить:

myHelper onNothing f = awaitForever $ maybe onNothing yield . f
person Michael Snoyman    schedule 05.06.2014
comment
У меня сложилось впечатление, что оба этих Ничто удалили, я ошибаюсь? - person fread2281; 05.06.2014

Использование комбинатора Майкла вызывает только (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) два раза.

person fread2281    schedule 05.06.2014
comment
Вы могли бы сначала mapC maybeF соединить это с вашими застежками-молниями. - person Michael Snoyman; 06.06.2014