Как объединить двух потребителей в один в Haskell Pipes?

Я использую библиотеку обработки потока Haskell pipes, чтобы написать инструмент командной строки. Каждое действие в командной строке может выводить результат в stdout и записывается в stderr с помощью pipes API.

Мне нужно Consumer, у которого есть тип Consumer (Either String String) m r для печати фрагмента данных (Left до stderr, Right до stdout) с помощью одного Consumer.

Код, который я написал (должен быть улучшен)

Эта функция consumeEither не обладает гибкостью, поэтому я хочу ее улучшить.

consumeEither :: (MonadIO m) => Consumer (Either String String) m ()
consumeEither = do
  eitherS <- await
  case eitherS of
    (Left l)  -> for (yield l) (liftIO . (IO.hPutStrLn IO.stderr))
    (Right r) -> for (yiled r) (liftIO . putStrLn)

Кроме того, было бы полезно предоставить функцию, которая принимает два Consumer и объединить их в один Consumer.

Вопрос

Кто-нибудь знает хороший пример или реализацию следующего интерфейса?

merge :: (Monad m) => Consumer a m r -> Consumer b m r -> Consumer (Either a b) m r
  • 1-й аргумент как stderr
  • 2-й аргумент как stdout

Использование функции

import           Pipes
import qualified Pipes.Prelude as P
import qualified System.IO as IO

stdoutOrErr :: Consumer (Either String String) IO ()
stdoutOrErr = merge (P.toHandle IO.stderr) P.stdoutLn

Спасибо

Ответ 1

(Это ответ @Michael, но я хотел бы написать его здесь, чтобы мы могли перенести вопрос из оставшейся без очереди очереди для тега Haskell.)

См. (+++) в pipes-extras. Имейте в виду, что Consumer - это Pipe (в никуда), поэтому P.toHandle IO.stderr +++ P.stdoutLn :: MonadIO m => Pipe (Either String String) (Either b d) m ().

Чтобы получить Consumer, вам нужно будет избавиться от Lefts например, с помощью >-> P.concat или >-> P.drain. Есть более надежные и красивые способы сделать это с помощью Fold s.