2013-11-24 23 views
4
阻止

,我有以下组动作:错误检查中都在Haskell

action1 :: IO Bool 
action2 :: IO Bool 
action3 :: IO Bool 

有些行动只是一个行动的组成

complexAction = do 
    action1 
    action2 
    action3 

我需要的是检查结果施工每个动作并在虚假情况下返回False。我可以手动完成,但我确实知道haskell确实有工具来摆脱那种样板。

回答

2

我建议你用MaybeT monad变压器代替。使用它有很多优点,只是返回IO Bool

  • 你的行动可以有不同的类型和返回值(不只是真/假)。如果你不需要任何结果,只需使用MaybeT IO()
  • 后来的人可以依靠前面的结果。
  • 由于MaybeT产生的单子是MonadPlus的实例,因此您可以使用所有monad plus操作。即mzero为失败的行动和x mplus y,这将运行y iff x失败。

轻微的缺点是,你必须lift所有IO行动MaybeT IO。这可以通过将您的操作编写为MonadIO m => ... -> m a而不是... -> IO a来解决。

例如:

import Control.Monad 
import Control.Monad.IO.Class 
import Control.Monad.Trans 
import Control.Monad.Trans.Maybe 

-- Lift print and putStrLn 
print' :: (MonadIO m, Show a) => a -> m() 
print' = liftIO . print 

putStrLn' :: (MonadIO m) => String -> m() 
putStrLn' = liftIO . putStrLn 

-- Add something to an argument 
plus1, plus3 :: Int -> MaybeT IO Int 
plus1 n = print' "+1" >> return (n + 1) 
plus3 n = print' "+3" >> return (n + 3) 

-- Ignore an argument and fail 
justFail :: Int -> MaybeT IO a 
justFail _ = mzero 

-- This action just succeeds with() or fails. 
complexAction :: MaybeT IO() 
complexAction = do 
    i <- plus1 0 
    justFail i -- or comment this line out <----------------< 
    j <- plus3 i 
    print' j 

-- You could use this to convert your actions to MaybeT IO: 
boolIOToMaybeT :: IO Bool -> MaybeT IO() 
boolIOToMaybeT x = do 
    r <- lift x 
    if r then return() else mzero 
-- Or you could have even more general version that works with other 
-- transformers as well: 
boolIOToMaybeT' :: (MonadIO m, MonadPlus m) => IO Bool -> m() 
boolIOToMaybeT' x = do 
    r <- liftIO x 
    if r then return() else mzero 

main :: IO() 
main = runMaybeT complexAction >>= print' 
3

最简单的方法是

complexAction = fmap and (sequence [action1, action2, action3]) 

但你也可以编写自己的组合子的第一个动作后停止:

(>>/) :: Monad m => m Bool -> m Bool -> m Bool 
a >>/ b = do 
    yes <- a 
    if yes then b else return False 

你会想声明的固定性,使之联想

infixl 1 >>/ 

然后你可以做

complexAction = action1 >>/ action2 >>/ action3 
+0

第一种方法让我有点紧张,因为它依赖于评估顺序,这意味着它依赖于'和'的实现细节。 –

+3

@RobinGreen不,它不是 - “序列”按顺序运行所有单子动作,然后是结果。您不能通过应用纯函数来混淆“IO”操作的评估顺序;不可能。 – 2013-11-24 10:09:24

+0

为true,所有操作都会运行,但thunk不一定全部运行。我误解了。 –

2

正如彼得说,任何东西,但一个狭窄的,含有的情况下,你就要去接线代码进行适当的错误从一开始就处理几乎可以肯定更好。我知道我经常后悔没有这样做,谴责自己进行一些非常乏味的重构。

如果可以的话,我想推荐Gabriel Gonzalez的errors软件包,它在Haskell的各种错误处理机制上强加了一些与传统相关的一致性。它允许你通过你的代码探测EitherEither是一个很好的捕获错误的类型。 (相比之下,Maybe将失去错误边信息。)一旦你安装的软件包,你可以这样写:

module Errors where 

import Control.Error 
import Data.Traversable (traverse) 

data OK = OK Int deriving (Show) 

action1, action2, action3 :: IO (Either String OK) 
action1 = putStrLn "Running action 1" >> return (Right $ OK 1) 
action2 = putStrLn "Running action 2" >> return (Right $ OK 2) 
action3 = putStrLn "Running action 3" >> return (Left "Oops on 3") 

runStoppingAtFirstError :: [IO (Either String OK)] -> IO (Either String [OK]) 
runStoppingAtFirstError = runEitherT . traverse EitherT 

...与像

*Errors> runStoppingAtFirstError [action1, action2] 
Running action 1 
Running action 2 
Right [OK 1,OK 2] 
*Errors> runStoppingAtFirstError [action1, action3, action2] 
Running action 1 
Running action 3 
Left "Oops on 3" 

输出(但要注意的是,计算在这里停在第一个错误,并且不当兵,直到血战到底 - 这可能不是你本来想什么errors包肯定是wide-其他许多变化也是可能的。)

+0

嗯,我不喜欢这种方法,因为我不知道如何处理返回的错误信息。我是新来的哈斯克尔,但我认为,而不是返回它更好地把它放到记录器单子。 – kubivan