如何在Haskell中实现控制流

如何在Haskell中实现控制流,haskell,monads,control-flow,either,Haskell,Monads,Control Flow,Either,我马上举个例子说明我想做什么 version1 :: IO () version1 = if boolCheck then case maybeCheck of Nothing -> putStrLn "Error: simple maybe failed" Just v -> case eitherCheck of Left e -> putStrLn $ "E

我马上举个例子说明我想做什么

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
这些只是例子。 也可以自由地考虑单子,
EitherT
或一个单子列表,其中我提取
head
,当它不是单子时失败

现在我正试图改进上述实现,我想到了
或者
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"
2) 始终使用明确的案例,甚至不使用

3) 定义我自己的monad来阻止
滥用。。。我可以提供所有的转换函数(如果还没有人做过类似的事情)

4) 在可能的情况下使用

5)

“定义助手函数”正是我处理这个问题的方式。除了满足
Bool
函数之外,该库已经提供了许多功能。对于那些我愿意


当然,在可能的范围内,您应该促进您正在调用的操作具有适当的多态性,以便不需要转换。

使用
maybe
以及
mtl
包。顺便说一句,
eitherCheck::Show a=>b
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")
{-#语言灵活上下文}
newtype Error=错误字符串
挑战::MonadError错误m=>m()
挑战
除非布尔检查(抛出“简单布尔检查失败”)

_所以我可能会首先将您的
version2
修改为

import Control.Monad.Trans
import Control.Monad.Trans.Either hiding (left, right)
import Control.Monad
import Control.Applicative
import Control.Arrow

version3 :: IO ()
version3 = eitherT onFailure onSuccess $ do
    guard boolCheck <|> fail "simple bool check failed"
    v <- hoistEither $ maybe (Left "simple maybe check failed") Right maybeCheck
    w <- hoistEither . left show $ eitherCheck
    lift (guard =<< monadicBoolCheck v) <|> fail "monadic boolcheck failed"
  where
    onFailure msg = putStrLn $ "Error: "++msg
    onSuccess _   = print "successfully doing the thing"
import Control.Monad.Trans
import Control.Monad.Trans.or隐藏(左、右)
进口管制
导入控制
导入控制。箭头
版本3::IO()
version3=成功$do时失败
guard boolCheck fail“简单bool检查失败”
要么是e a->要么是e'm a
用f=提升器提升。左f

要想在这里找到所有可能的选项,请查看以下要点:

它定义了几个辅助函数,基本上结合了
可能
或者
,以及与
投掷者
等。并产生这样的代码

gauntlet :: MonadError Error m => m (a, b, c)
gauntlet = do
    assertTrue boolCheck $ Error "simple bool check failed"
    v <- assertJust maybeCheck $ Error "simple maybe check failed"
    assertNothing maybeCheck' $ Error . show
    w <- assertRight eitherCheck $ Error . show
    b <- monadicBoolCheck
    assertTrue b $ Error "monadic bool check failed"
    x <- assertSingletonList list $ Error "list not singleton"
    pure (v, w, x)

version3 :: IO ()
version3 = putStrLn $
  case gauntlet of
    Left  (Error e) -> "Error: " ++ e
    Right result    -> "successfully doing thing with result"
gauntlet::MonadError错误m=>m(a、b、c)
挑战
assertTrue boolCheck$错误“简单布尔检查失败”

很好。Foor
Bool
类似“除非boolCheck$Left”这样的“检查失败”总是更好。我们这样做了,我喜欢这样,每个检查都变成一个线性,实际控制流是最简洁的。然而,当我向其他人展示代码(有点像这样)时,我意识到很难解释它的功能,而且那些助手函数往往非常技术性和特殊性。如果,再一次,大家一致认为这些助手函数是未来的发展方向,我会支持它。甚至可以将它们放入一个类型classs
ToEither
,并定义一个操作符来允许code
anyWeirdTypedCheck | |!“此检查失败”
Bool
和组合类型(如
(Bool,a)
)转换为它们可能会破坏创建类型类的想法
version4 :: IO ()
version4 = eitherT onFailure onSuccess $ do
    failUnless "simple bool check failed" boolCheck
    v <- hoistMaybe "simple maybe check failed" maybeCheck
    w <- hoistEitherWith show eitherCheck
    failUnless "monadic boolcheck failed" =<< lift (monadicBoolCheck v)
  where
    onFailure msg = putStrLn $ "Error: "++msg
    onSuccess _   = print "successfully doing the thing"

failUnless :: Monad m => String -> Bool -> m ()
failUnless _ True = return ()
failUnless msg _ = fail msg

hoistMaybe :: Monad m => e -> Maybe a -> EitherT e m a
hoistMaybe err = hoistEither . maybe (Left err) Right

hoistEitherWith :: Monad m => (e -> e') -> Either e a -> EitherT e' m a
hoistEitherWith f = hoistEither . left f
gauntlet :: MonadError Error m => m (a, b, c)
gauntlet = do
    assertTrue boolCheck $ Error "simple bool check failed"
    v <- assertJust maybeCheck $ Error "simple maybe check failed"
    assertNothing maybeCheck' $ Error . show
    w <- assertRight eitherCheck $ Error . show
    b <- monadicBoolCheck
    assertTrue b $ Error "monadic bool check failed"
    x <- assertSingletonList list $ Error "list not singleton"
    pure (v, w, x)

version3 :: IO ()
version3 = putStrLn $
  case gauntlet of
    Left  (Error e) -> "Error: " ++ e
    Right result    -> "successfully doing thing with result"