Running `sequence` over a conduit

137 views Asked by At

I have a function of the effect:

f :: a -> Either b c

I want to build a conduit of this type:

ConduitT a c m (Maybe b)

Basically, we've got a stream of as, and I want produce a stream of cs, but fail fast on error b.

If I was using lists, I'd be basically making a function

[a] -> Either b [c]

Which would be easy, just sequence . (map f).

But I'm having trouble converting this to conduits. Any ideas?

1

There are 1 answers

0
K. A. Buhr On BEST ANSWER

First, consider if you really want to return Maybe b, or if you want to raise the error in the monad m. If the latter, then you may actually want:

mapMC :: Monad m => (a -> m b) -> ConduitT a b m ()

which directly converts an f :: a -> Either b c to:

mapMC f :: ConduitT a c (Either b) ()

or for a more general m with an ExceptT transformer in the stack:

mapMC (liftEither . f) :: (MonadError b m) => ConduitT a c m ()

If you decide that, no, you really want a Maybe b return type, then the following will probably work:

import Conduit

whileRightC :: (Monad m) => (a -> Either b c) -> ConduitT a c m (Maybe b)
whileRightC f = mapAccumWhileC step Nothing
  where step a _ = case f a of Right c -> Right (Nothing, c)
                               Left  b -> Left  (Just b)

Alternatively, you can use the functions in Data.Conduit.Lift like so to convert a monadic exception generated by mapMC into an Either value that you can convert to a Maybe:

import Conduit
import Control.Monad.Trans.Except (except)

whileRightC :: (Monad m) => (a -> Either b c) -> ConduitT a c m (Maybe b)
whileRightC f = do
  r <- runExceptC $ mapMC (except . f)
  return $ case r of
    Left err -> Just err
    Right () -> Nothing

If performance is critical, you might want to benchmark these two versions of whileRightC to see which is faster.