Is there a way to write this function in a more "monadic" way, instead of resorting to pattern matching on Either
?
{-# LANGUAGE LambdaCase #-}
calculate :: (Monad m) => (a -> m (Either e b)) -> Either e a -> m (Either e b)
calculate f = \case
Left err -> return $ Left err
Right vals -> f vals
Specifically, for my use case, m
is IO
; f
is a function that takes in input and produces some IO
effect or fails, and the input is something that could have failed already.
Maybe using ExceptT
?
CodePudding user response:
Yep, looks like ExceptT
to me. Though I would probably not use a function with this signature -- instead, I would use ExceptT
more broadly, and then this function is just (=<<)
. Of course this is guesswork based on the use case.
But if you must:
calculate :: (Monad m) => (a -> m (Either e b)) -> Either e a -> m (Either e b)
calculate f m = runExceptT (ExceptT . f =<< ExceptT m)
CodePudding user response:
You can use traverse
and join
:
calculate :: (Monad f, Traversable f, Applicative m) => (a1 -> m (f a2)) -> f a1 -> m (f a2)
calculate f e = join <$> traverse f e
Note the more general type signature. That's what GHC infers. Instead of Either e
any type is sufficient as long as it has instances for Monad
and Traversable
. Also, m
doesn't need to have a Monad
, Applicative
is enough.
ExceptT
works as well (the first one doesn't change the type of the result, the second one goes all in on ExceptT
):
calculate :: (Monad m) => (a -> ExceptT e m b) -> Either e a -> m (Either e b)
calculate f e = runExceptT $ ExceptT (pure e) >>= f
calculate2 :: (Monad m) => (a -> ExceptT e m b) -> Either e a -> ExceptT e m b
calculate2 f e = ExceptT (pure e) >>= f
I personally would prefer the former, because I find it easier to grasp without ExceptT
.