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 a
s, and I want produce a stream of c
s, 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.