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.
