Error checking inside a do block in Haskell

i has the following set of actions:

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

some actions are just part of other actions

complexAction = do
  action1
  action2
  action3

I need a construct that checks the result of each action and returns False if false. I can do this manually, but I know for sure that haskell has tools to get rid of this type of template.

+4
source share
3 answers

I would suggest using a MaybeTmonad transformer instead. Using it has many advantages over returning IO Bool:

  • Your actions can have different types and return values โ€‹โ€‹(and not just true / false). If you do not need any results, just use 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'
+2

-

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
+3

, -, , . , , , .

, Gabriel Gonzalez errors, Haskell, . Either , Either - . ( , 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"

(But note that the calculation here stops with the first error and does not work until the end of the bitter end - which may not be what you wanted. The package errorsis certainly wide enough for many other options to be possible.)

+2
source

All Articles