Search code examples
haskellconduit

Running `sequence` over a conduit


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?


Solution

  • 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.