8

我有两个值,t1t2,类型为Either String Type。-valueLeft用于错误处理。这些值在返回的函数中使用Either String Type

我想要做的是检查两者是否t1t2都是Right-values 和满足p :: Type -> Bool。如果他们这样做,我想返回Right (the type inside t1)。如果t1t2都是Right- 值,但不满足p,我想返回Left someString。如果其中一个t1ort2是一个Left值,我只想传递该值。

我怎样才能以优雅的方式做到这一点?我有一种预感,使用 Either 作为 monad 是正确的做法,但我不确定如何去做。

4

4 回答 4

12

为什么是单子?

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
于 2011-06-09T09:39:24.610 回答
5

如果你确实想用 a 来做,Monad它看起来像这样,但是最近更改了Monadfor 的实例,Either因此这在最近的 GHC 中实际上不起作用:

do v1 <- t1
   v2 <- t2
   guard (p v1 && p v2) `mplus` Left someString
   return v1
于 2011-06-09T09:30:29.050 回答
4

您可以创建自己的错误数据类型并使其成为 Monad 的实例。

data Computation a = Error String | Result a


instance Monad Computation where
    (Result x)  >>= k   =  k x
  e@(Error a)   >>= k   =  e

然后使用Ganesh Sittampalam 描述的方法。(您还需要添加一个实例 MonadPlus Computation。

更新完整性,它看起来像这样:

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
于 2011-06-09T10:49:48.113 回答
1

Left如果您真的想要,您可以将单子动作与值的传播分开:

import Control.Monad
import Control.Applicative
import Control.Monad.Instances

这产生了简单的一元动作:

foo :: Type -> Type -> Either String Type
foo t1 t2 | p t1 && p t2 = Right t1
          | otherwise    = Left somestring

您可以将其应用于单子参数以获得所需的功能,使用

fooM :: Either String Type -> Either String Type -> Either String Type
fooM t1 t2 = join (foo <$> t1 <*> t2)

或等效地

fooM t1 t2 = do
    a <- t1
    b <- t2
    foo a b
于 2011-06-09T20:11:00.543 回答