开发者

Using 'Either' in Haskell

开发者 https://www.devze.com 2023-03-11 06:44 出处:网络
I have two values, t1 and t2, of type Either String Type. The Left-value is used for error handling. These values are used in a function which returns Either String Type.

I have two values, t1 and t2, of type Either String Type. The Left-value is used for error handling. These values are used in a function which returns Either String Type.

What I want to do is check if both t1 and t2 are Right-values and satisfy p :: Type -> Bool. If they do, I want to return Right (the type inside t1). If both t1 and t2 are Right-values, but do not satisfy p, I w开发者_开发技巧ant to return Left someString. If one of t1 or t2 is a Left 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.


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


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


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


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
0

精彩评论

暂无评论...
验证码 换一张
取 消