[英]How to do control flow in Haskell
我將舉例說明我想立刻做些什么。
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
Maybe, EitherT
或aa singleton列表,我提取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
,但可讀性的提高可能是微不足道的。 在添加進一步檢查時,Version2更勝一籌。
有這種最終的優雅方式嗎?
我不喜歡的:
1)我部分地濫用了Either
monad而我實際上做的更像是一個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
2)始終使用明確的案例,甚至不使用hoistEither
3)定義我自己的monad以阻止Either
濫用......我可以提供所有轉換功能(如果沒有人已經做過類似的話)
4)使用maybe
和either
在可能的情況
5)......?
maybe
either
和mtl
包。 通過by, eitherCheck :: Show a => Either ab
的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")
“定義輔助函數”正是我將如何處理它。 錯誤庫已經提供了許多,可能的例外是滿足Bool
函數。 對於那些我只會when
/ unless
when
使用的人 。
當然,在可能的范圍內,您應該將您所要求的操作推廣為適當的多態,以便不需要轉換。
所以我可能首先將你的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"
我發現它更具可讀性,但仍然有點尷尬,所以如果我做了很多像這樣的代碼,我會介紹一些幫助:
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
為了獲得全部可能的選項,請查看以下要點:
https://gist.github.com/rubenmoor/c390901247e4e7bb97cf
它定義了一些輔助功能,基本上結合maybe
, either
和這種與throwError
。 並得到像這樣的代碼。
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"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.