我想我理解如何级联同一类型的Monad。我想将两个Monad组合在一起,根据它们执行操作:
我认为下面的代码恢复了问题:假设我们有一个函数来验证String包含" Jo"并附加" Bob"如果是这样的话,另一个验证字符串长度是> 8
hello函数将应用第一个,然后第二个应用于第一个结果并返回" Hello"对于所有那些在成功的情况下或者没有什么' (如果发生错误,我不知道这是什么,没有什么,不管是左边还是什么都没有。
我相信它是Monad变压器所需要的,但是我找不到一个可以帮助我开始的简洁例子。
我确切地认为这不是理论上的,因为Haskell包与Aither和其他与Maybe一起工作的包有效
validateContainsJoAndAppendBob :: String -> Maybe String
validateContainsJoAndAppendBob l =
case isInfixOf "Jo" l of
False -> Nothing
True -> Just $ l ++ "Bob"
validateLengthFunction :: Foldable t => t a -> Either String (t a)
validateLengthFunction l =
case (length l > 8) of
False -> Left "to short"
True -> Right l
-- hello l = do
-- v <- validateContainsJoAndAppendBob l
-- r <- validateLengthFunction v
-- return $ "Hello " ++ r
答案 0 :(得分:6)
使用函数将Maybe
转换为Either
note :: Maybe a -> e -> Either e a
note Nothing e = Left e
note (Just a) _ = Right a
hello l = do
v <- validateContainsJoAndAppendBob l `note` "Does not contain \"Jo\""
r <- validateLengthFunction v
return $ "Hello " ++ r
答案 1 :(得分:5)
你想要的是(在分类意义上)从Maybe
到Either String
的自然转换,maybe
函数可以提供。
maybeToEither :: e -> Maybe a -> Either e a
maybeToEither e = maybe (Left e) Right
hello l = do
v <- maybeToEither "No Jo" (validateContainsJoAndAppendBob l)
r <- validateLengthFunction v
return $ "Hello " ++ r
您可以使用<=<
中的Control.Monad
撰写两个验证码。
hello l = do
r <- validateLengthFunction <=< maybeToEither "No Jo" . validateContainsJoAndAppendBob $ l
return $ "Hello " ++ r
你也可以使用>=>
和return
将整个事物变成一个怪异的无点定义。
hello = maybeToEither "No Jo" . validateContainsJoAndAppendBob
>=> validateLengthFunction
>=> return . ("Hello " ++)
答案 2 :(得分:4)
除了李尧瑶给出的实际答案外,还有其他选择。这是两个。
Maybe a
与Either () a
同构,这意味着两者之间存在无损翻译:
eitherFromMaybe :: Maybe a -> Either () a
eitherFromMaybe (Just x) = Right x
eitherFromMaybe Nothing = Left ()
maybeFromEither :: Either () a -> Maybe a
maybeFromEither (Right x) = Just x
maybeFromEither (Left ()) = Nothing
您可以使用其中一个转换为另一个。由于validateLengthFunction
在失败时返回错误文本,因此将其返回值转换为Maybe String
值将是一种有损转换,因此最好使用eitherFromMaybe
。
但问题是,这只会为您提供Either () String
值,而您需要Either String String
。您可以通过利用Either
作为Bifunctor
实例来解决此问题。首先,
import Data.Bifunctor
然后您可以将hello
写为:
hello :: String -> Either String String
hello l = do
v <-
first (const "Doesn't contain 'Jo'.") $
eitherFromMaybe $
validateContainsJoAndAppendBob l
r <- validateLengthFunction v
return $ "Hello " ++ r
这与Li-yao Xia的答案基本相同 - 虽然不太实际,但也不那么特别。
first
函数映射Either
值的第一个(最左侧)个案。在这种情况下,如果validateContainsJoAndAppendBob
的返回值为Left
值,则它始终为Left ()
,因此您可以使用const
忽略()
输入并返回String
值。
这可以完成工作:
*Q49816908> hello "Job, "
Left "to short"
*Q49816908> hello "Cool job, "
Left "Doesn't contain 'Jo'."
*Q49816908> hello "Cool Job, "
Right "Hello Cool Job, Bob"
这个替代方案我更喜欢下一个,但仅仅是为了完整性:
另一个选择是使用Monad变形金刚。您可以将Maybe
包裹在EitherT
中,也可以相反地将Either
包裹在MaybeT
中。以下示例执行后者。
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (MaybeT(..))
helloT :: String -> MaybeT (Either String) String
helloT l = do
v <- MaybeT $ return $ validateContainsJoAndAppendBob l
r <- lift $ validateLengthFunction v
return $ "Hello " ++ r
这也有效,但您仍需要处理Just
,Nothing
,Left
和Right
的各种组合:
*Q49816908> helloT "Job, "
MaybeT (Left "to short")
*Q49816908> helloT "Cool job, "
MaybeT (Right Nothing)
*Q49816908> helloT "Cool Job, "
MaybeT (Right (Just "Hello Cool Job, Bob"))
如果您要剥离MaybeT
包装,可以使用runMaybeT
:
*Q49816908> runMaybeT $ helloT "Cool Job, "
Right (Just "Hello Cool Job, Bob")
在大多数情况下,我可能会选择第一个选项......