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