1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
{-| This module provides 'throwE' and 'catchE' for 'Either'. These two
functions reside here because 'throwE' and 'catchE' correspond to 'return'
and ('>>=') for the flipped 'Either' monad: 'EitherR'. Similarly, this
module defines 'throwT' and 'catchT' for 'EitherT', which correspond to the
'Monad' operations for 'EitherRT'.
These throw and catch functions improve upon @MonadError@ because:
* 'catch' is more general and allows you to change the left value's type
* They are Haskell98
More advanced users can use 'EitherR' and 'EitherRT' to program in an
entirely symmetric \"success monad\" where exceptional results are the norm
and successful results terminate the computation. This allows you to chain
error-handlers using @do@ notation and pass around exceptional values of
varying types until you can finally recover from the error:
> runEitherRT $ do
> e2 <- ioExceptionHandler e1
> bool <- arithmeticExceptionhandler e2
> when bool $ lift $ putStrLn "DEBUG: Arithmetic handler did something"
If any of the above error handlers 'succeed', no other handlers are tried.
If you choose not to typefully distinguish between the error and sucess
monad, then use 'flipE' and 'flipET', which swap the type variables without
changing the type.
-}
module Data.EitherR (
-- * EitherR
EitherR(..),
-- ** Operations in the EitherR monad
succeed,
-- ** Conversions to the Either monad
throwE,
catchE,
handleE,
fmapL,
-- ** Flip alternative
flipE,
-- * EitherRT
EitherRT(..),
-- ** Operations in the EitherRT monad
succeedT,
-- ** Conversions to the EitherT monad
throwT,
catchT,
handleT,
fmapLT,
-- ** Flip alternative
flipET,
) where
import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))
import Control.Monad (liftM, ap, MonadPlus(mzero, mplus))
import Control.Monad.Trans.Class (MonadTrans(lift))
import Control.Monad.IO.Class (MonadIO(liftIO))
import Control.Monad.Trans.Either (EitherT(EitherT, runEitherT), left, right)
import Data.Monoid (Monoid(mempty, mappend))
{-| If \"@Either e r@\" is the error monad, then \"@EitherR r e@\" is the
corresponding success monad, where:
* 'return' is 'throwE'.
* ('>>=') is 'catchE'.
* Successful results abort the computation
-}
newtype EitherR r e = EitherR { runEitherR :: Either e r }
instance Functor (EitherR r) where
fmap = liftM
instance Applicative (EitherR r) where
pure = return
(<*>) = ap
instance Monad (EitherR r) where
return e = EitherR (Left e)
EitherR m >>= f = case m of
Left e -> f e
Right r -> EitherR (Right r)
instance (Monoid r) => Alternative (EitherR r) where
empty = EitherR (Right mempty)
e1@(EitherR (Left _)) <|> _ = e1
_ <|> e2@(EitherR (Left _)) = e2
EitherR (Right r1) <|> EitherR (Right r2)
= EitherR (Right (mappend r1 r2))
instance (Monoid r) => MonadPlus (EitherR r) where
mzero = empty
mplus = (<|>)
-- | Complete error handling, returning a result
succeed :: r -> EitherR r e
succeed r = EitherR (return r)
-- | 'throwE' in the error monad corresponds to 'return' in the success monad
throwE :: e -> Either e r
throwE e = runEitherR (return e)
-- | 'catchE' in the error monad corresponds to ('>>=') in the success monad
catchE :: Either a r -> (a -> Either b r) -> Either b r
e `catchE` f = runEitherR $ EitherR e >>= \a -> EitherR (f a)
-- | 'catchE' with the arguments flipped
handleE :: (a -> Either b r) -> Either a r -> Either b r
handleE = flip catchE
-- | Map a function over the 'Left' value of an 'Either'
fmapL :: (a -> b) -> Either a r -> Either b r
fmapL f = runEitherR . fmap f . EitherR
-- | Flip the type variables of 'Either'
flipE :: Either a b -> Either b a
flipE e = case e of
Left a -> Right a
Right b -> Left b
-- | 'EitherR' converted into a monad transformer
newtype EitherRT r m e = EitherRT { runEitherRT :: EitherT e m r }
instance (Monad m) => Functor (EitherRT r m) where
fmap = liftM
instance (Monad m) => Applicative (EitherRT r m) where
pure = return
(<*>) = ap
instance (Monad m) => Monad (EitherRT r m) where
return e = EitherRT (left e)
m >>= f = EitherRT $ EitherT $ do
x <- runEitherT $ runEitherRT m
runEitherT $ runEitherRT $ case x of
Left e -> f e
Right r -> EitherRT (right r)
instance (Monad m, Monoid r) => Alternative (EitherRT r m) where
empty = EitherRT $ EitherT $ return $ Right mempty
e1 <|> e2 = EitherRT $ EitherT $ do
x1 <- runEitherT $ runEitherRT e1
case x1 of
Left l -> return (Left l)
Right r1 -> do
x2 <- runEitherT $ runEitherRT e2
case x2 of
Left l -> return (Left l)
Right r2 -> return (Right (mappend r1 r2))
instance (Monad m, Monoid r) => MonadPlus (EitherRT r m) where
mzero = empty
mplus = (<|>)
instance MonadTrans (EitherRT r) where
lift = EitherRT . EitherT . liftM Left
instance (MonadIO m) => MonadIO (EitherRT r m) where
liftIO = lift . liftIO
-- | Complete error handling, returning a result
succeedT :: (Monad m) => r -> EitherRT r m e
succeedT r = EitherRT (return r)
-- | 'throwT' in the error monad corresponds to 'return' in the success monad
throwT :: (Monad m) => e -> EitherT e m r
throwT e = runEitherRT (return e)
-- | 'catchT' in the error monad corresponds to ('>>=') in the success monad
catchT :: (Monad m) => EitherT a m r -> (a -> EitherT b m r) -> EitherT b m r
e `catchT` f = runEitherRT $ EitherRT e >>= \a -> EitherRT (f a)
-- | 'catchT' with the arguments flipped
handleT :: (Monad m) => (a -> EitherT b m r) -> EitherT a m r -> EitherT b m r
handleT = flip catchT
-- | Map a function over the 'Left' value of an 'EitherT'
fmapLT :: (Monad m) => (a -> b) -> EitherT a m r -> EitherT b m r
fmapLT f = runEitherRT . fmap f . EitherRT
-- | Flip the type variables of an 'EitherT'
flipET :: (Monad m) => EitherT a m b -> EitherT b m a
flipET = EitherT . liftM flipE . runEitherT
|