Использование "Либо" в 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