Использование «Либо» в Haskell - PullRequest
6 голосов
/ 09 июня 2011

У меня есть два значения, t1 и t2, типа Either String Type.Значение Left используется для обработки ошибок.Эти значения используются в функции, которая возвращает Either String Type.

. Я хочу проверить, являются ли t1 и t2 значениями Right и удовлетворяют ли p :: Type -> Bool.Если они это сделают, я хочу вернуть Right (the type inside t1).Если t1 и t2 являются Right -значениями, но не удовлетворяют p, я хочу вернуть Left someString.Если одно из t1 или t2 является значением Left, я просто хочу передать это значение.

Как я могу сделать это элегантным способом?У меня есть предчувствие, что использование Either в качестве монады - правильное решение, но я не уверен, как это сделать.

Ответы [ 4 ]

10 голосов
/ 09 июня 2011

Почему монады?

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
5 голосов
/ 09 июня 2011

Если вы хотите сделать это с Monad, это выглядело бы примерно так, но экземпляр Monad для Either был недавно изменен, так что это на самом деле не будет работать в последних GHC:

do v1 <- t1
   v2 <- t2
   guard (p v1 && p v2) `mplus` Left someString
   return v1
3 голосов
/ 09 июня 2011

Вы можете создать свой собственный тип данных Error и сделать его экземпляром Monad.

data Computation a = Error String | Result a


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

А затем используйте метод , описанный Ганеш Ситтампалам . (Вам также нужно будет добавить экземпляр 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
1 голос
/ 10 июня 2011

Вы можете отделить монадическое действие от распространения значений 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
...