Using 'Either' in Haskell
Solution 1
Why monads?
test p (Right t1) (Right t2) | p t1 && p t2 = Right t1
| otherwise = Left "nope"
test _ (Left t1) _ = Left t1
test _ _ (Left t2) = Left t2
Solution 2
If you do want to do it with a Monad
it would look something like this, but the Monad
instance for Either
was recently changed so that this won't actually work in recent GHCs:
do v1 <- t1
v2 <- t2
guard (p v1 && p v2) `mplus` Left someString
return v1
Solution 3
You could create your own Error datatype and make it instance of Monad.
data Computation a = Error String | Result a
instance Monad Computation where
(Result x) >>= k = k x
e@(Error a) >>= k = e
And then use the method described by Ganesh Sittampalam. (You will need to add an instance MonadPlus Computation too.
Update for completeness it would look like this:
import Control.Monad
data Computation a = Error String | Result a
instance Monad Computation where
return a = Result a
(Result x) >>= k = k x
(Error a) >>= k = Error a
instance MonadPlus Computation where
mzero = Error "Always fail"
mplus (Error a) r = r
mplus l _ = l
check :: (Int -> Bool) -> Computation Int
check p = do v1 <- Result 4
v2 <- Result 2
guard (p v1 && p v2) `mplus` Error "someString"
return v1
Solution 4
You can separate out the monadic action from the propagation of Left
values if you really want to:
import Control.Monad
import Control.Applicative
import Control.Monad.Instances
This yields the simple monadic action:
foo :: Type -> Type -> Either String Type
foo t1 t2 | p t1 && p t2 = Right t1
| otherwise = Left somestring
Which you can apply to monadic arguments to get the function you want, using
fooM :: Either String Type -> Either String Type -> Either String Type
fooM t1 t2 = join (foo <$> t1 <*> t2)
or equivalently
fooM t1 t2 = do
a <- t1
b <- t2
foo a b
Comments
-
Viktor Dahl almost 2 years
I have two values,
t1
andt2
, of typeEither String Type
. TheLeft
-value is used for error handling. These values are used in a function which returnsEither String Type
.What I want to do is check if both
t1
andt2
areRight
-values and satisfyp :: Type -> Bool
. If they do, I want to returnRight (the type inside t1)
. If botht1
andt2
areRight
-values, but do not satisfyp
, I want to returnLeft someString
. If one oft1
ort2
is aLeft
value, I just want to pass on that value.How can I do this in an elegant way? I have a hunch that using Either as a monad is the right thing to do, but I'm not sure of how to go about it.
-
Bayquiri almost 13 yearsIt does work with GHC 7.0.3 if I import
Control.Monad.Error
frommtl
package. One can also complete abstraction fromEither
by replacingLeft
withthrowError . strMsg
. Not that it's terribly useful though. -
mightybyte almost 13 yearsCan you give more details on the change, specifically what changed and when?
-
Tsuyoshi Ito almost 13 yearsYour definition of
mplus
does not do what you want it to do. -
Alessandro Vermeulen almost 13 years@Tsuyoshi Ito, I fxied the code so mplus shows proper behaviour.
-
Antoine Latter almost 13 yearsmightybyte: the only change I know of is that 'fail' no longer maps to 'Left'. I'm not sure how that affects the above example, though.
-
Edward Kmett almost 13 years@Antoine Latter:
guard False = mzero
, butEither a
is not an instance ofMonadPlus
as the instance is defined inControl.Monad.Instances
and does not have access to theError
class from theControl.Monad.Trans.Error
which is out intransformers
.mtl
then re-exports the instance viaControl.Monad.Error
. -
Edward Kmett almost 13 years@mightybyte: We moved the instance for
Either
fromtransformers
(and before thatmtl
) intobase
, but as the instance involves both a class and a type from the Prelude and isn't stated by any current Haskell standard, we had to export it fromControl.Monad.Instances
rather than just put it into scope everywhere. In the process we removed the assumption ofError
from the left hand argument toEither
. This had the side-effect of changing the behavior offail
, fromLeft . strMsg
toerror
. We leftMonadPlus
andAlternative
intransformers
. -
Edward Kmett almost 13 years@mightybyte: As to when, about a year or so back, we had the discussion on the libraries mailing list.