Использование "Либо" в Haskell

У меня есть два значения, 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 ответа

Решение

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

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

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

do v1 <- t1
   v2 <- t2
   guard (p v1 && p v2) `mplus` Left someString
   return v1

Вы можете создать свой собственный тип данных 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

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