如何在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$错误“简单布尔检查失败”
很好。FoorBool
类似“除非boolCheck$Left”这样的“检查失败”总是更好。我们这样做了,我喜欢这样,每个检查都变成一个线性,实际控制流是最简洁的。然而,当我向其他人展示代码(有点像这样)时,我意识到很难解释它的功能,而且那些助手函数往往非常技术性和特殊性。如果,再一次,大家一致认为这些助手函数是未来的发展方向,我会支持它。甚至可以将它们放入一个类型classsToEither
,并定义一个操作符来允许codeanyWeirdTypedCheck | |!“此检查失败”
将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"