Home > Back-end >  Running `sequence` over a conduit
Running `sequence` over a conduit

Time:09-23

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?

CodePudding user response:

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.

  • Related