要进行错误处理,您不需要Either(T)
monad。你可以通过组合留在Applicative
里面。示例(为了好玩,AccValidation
它会累积所有错误):
import Control.Applicative
import Control.Monad.Error
import Data.Validation
import Data.Bifoldable
import Data.Functor.Compose
-- Replicating OP example with a Dummy monad (Get is made Applicative in newer libs)
data Dummy a = D a
deriving Show
instance Monad Dummy where
return = D
(D x) >>= f = f x
instance Functor Dummy where
fmap f (D x) = D (f x)
getM1 :: ErrorT String Dummy Int
getM1 = lift (D 1)
-- Can do with Applicatives + (Acc)Validation too
instance Applicative Dummy where
pure = return
(<*>) = ap
getA :: Compose Dummy (AccValidation String) Int
getA = Compose $ D (success 1)
getE :: Compose Dummy (AccValidation String) Int
getE = Compose $ D (failure "bad")
-- Applicative composition can work either way
getA2 :: Compose (AccValidation String) Dummy Int
getA2 = Compose $ success (D 1)
getE2 :: Compose (AccValidation String) Dummy Int
getE2 = Compose $ failure "bad"
main = do
runMonadic $ (+) <$> getM1 <*> getM1 -- D "2"
--
runApplicative $ (+) <$> getA <*> getA -- D "2"
runApplicative $ (+) <$> getE <*> getA -- D "bad"
runApplicative $ (+) <$> getE <*> getE -- D "badbad"
--
runOtherApp $ (+) <$> getA2 <*> getA2 -- "D 2"
runOtherApp $ (+) <$> getE2 <*> getE2 -- "badbad"
where
runMonadic = print . fmap (either id show) . runErrorT
runApplicative = print . fmap (validate id show) . getCompose
runOtherApp = print . validate id show . getCompose
-- some helper mimicking @either@ of @Either@
validate :: (e -> c) -> (a -> c) -> AccValidation e a -> c
validate f g = bifoldl (const f) (const g) undefined