如何在Haskell中控制流程

rub*_*oor 2 monads haskell either control-flow

我将举例说明我想立刻做些什么.

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"
Run Code Online (Sandbox Code Playgroud)

基本上我想在一些检查结果为正的情况下"做一件事".每当一次检查结果为否定时,我想保留有关违规检查的信息并中止任务.在现实生活中,这些检查有不同的类型,因此我打电话给他们

boolCheck        :: Bool
maybeCheck       :: Maybe a
eitherCheck      :: Show a => Either a b
monadicBoolCheck :: Monad m => m Bool
Run Code Online (Sandbox Code Playgroud)

这只是例子.随意也可以想到monadic Maybe,EitherT或者head是一个单例列表,当它不是单例时我提取并失败.

现在我正在努力改进上面的实现,Eithermonad进入我的脑海,因为它有一个错误消息中止的概念.

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"
Run Code Online (Sandbox Code Playgroud)

虽然我更喜欢version2,但可读性的提高可能是微不足道的.在添加进一步检查时,Version2更胜一筹.

有这种最终的优雅方式吗?

我不喜欢的:

1)我部分地滥用了Eithermonad,我实际上做的更像是一个Maybe带有卷的monad Just并且Nothing转换为monadic bind

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
Run Code Online (Sandbox Code Playgroud)

2)始终使用明确的案例,甚至不使用 hoistEither

3)定义我自己的monad以阻止Either滥用...我可以提供所有转换功能(如果没有人已经做过类似的事情)

4)使用maybeeither在可能的情况

5)......?

hao*_*hao 5

使用maybe,eithermtl包.通过by,eitherCheck :: Show a => Either a b's Show a约束可能不是你想要的:它允许调用者选择他们想要的任何类型,只要类型实现Show a.您可能打算a成为一种类型,以便调用者只能调用show该值.大概!

{-# LANGUAGE FlexibleContexts #-}

newtype Error = Error String

gauntlet :: MonadError Error m => m ()
gauntlet = do
  unless boolCheck (throw "simple bool check failed")
  _ <- maybe (throw "simple maybe check failed") pure maybeCheck
  _ <- either throw pure eitherCheck
  x <- monadicBoolCheck
  unless x (throw "monadic bool check failed")
  return ()
  where
    throw = throwError . Error

version2 :: IO ()
version2 =
  putStrLn (case gauntlet of
              Left (Error e) ->
                "Error: " ++ e
              Right _ ->
                "successfully doing thing")
Run Code Online (Sandbox Code Playgroud)