【发布时间】:2016-01-28 00:44:23
【问题描述】:
我将举一个我想马上做的例子。
version1 :: IO ()
version1 =
if boolCheck
then case maybeCheck of
Nothing -> putStrLn "Error: simple maybe failed"
Just v -> case eitherCheck of
Left e -> putStrLn $ "Error: " ++ show e
Right w -> monadicBoolCheck v >>= \case
False -> putStrLn "Error: monadic bool check failed"
True -> print "successfully doing the thing"
else putStrLn "simple bool check failed"
基本上我想在一些检查结果为肯定的情况下“做一件事”。 每当单次检查结果为负时,我想保留有关违规检查的信息并中止任务。 在现实生活中,这些支票有不同的类型,因此我称它们为
boolCheck :: Bool
maybeCheck :: Maybe a
eitherCheck :: Show a => Either a b
monadicBoolCheck :: Monad m => m Bool
这些只是示例。
也可以随意考虑单子 Maybe、EitherT 或一个单例列表,我在其中提取 head 并在它不是单例时失败。
现在我正在尝试改进上述实现,我想到了 Either monad,因为它具有中止并显示错误消息的概念。
version2 :: IO ()
version2 = do
result <- runEitherT $ do
if boolCheck
then pure ()
else left "simple bool check failed"
v <- case maybeCheck of
Just x -> pure x
Nothing -> left "simple maybe check failed"
w <- hoistEither . mapLeft show $ eitherCheck
monadicBoolCheck v >>= \case
True -> pure ()
False -> left "monadic bool check failed"
case result of
Left msg -> putStrLn $ "Error: " ++ msg
Right _ -> print "successfully doing the thing"
虽然我更喜欢version2,但可读性的提高可能是微不足道的。
在添加进一步检查方面,版本 2 更胜一筹。
是否有一种最终优雅的方式来做到这一点?
我不喜欢的:
1) 我部分滥用了Either monad,而我实际上做的更像是Maybe monad,在单子绑定中切换了Just 和Nothing
2) 将检查转换为Either 需要相当详细地使用case 或转换函数(如hoistEither)。
提高可读性的方法可能是:
1) 定义辅助函数以允许类似代码
v <- myMaybePairToEither "This check failed" monadicMaybePairCheck
monadicMaybePairCheck :: Monad m => m (Maybe x, y)
...
myMaybePairToEither :: String -> m (Maybe x, y) -> EitherT m e z
myMaybePairToEither _ (Just x, y) = pure $ f x y
myMaybePairToEither msg (Nothing, _) = left msg
2) 始终使用显式案例,甚至不使用hoistEither
3) 定义我自己的 monad 来阻止 Either 滥用...我可以提供所有的转换功能(如果没有人做过类似的事情)
4) 尽可能使用maybe 和either
5) ... ?
【问题讨论】:
标签: haskell monads control-flow either