Logical and rigor with the IO monad

I am trying to write a simple program in Haskell. It should basically run two shell commands in parallel. Here is the code:

import System.Cmd import System.Exit import Control.Monad exitCodeToBool ExitSuccess = True exitCodeToBool (ExitFailure _) = False run :: String -> IO Bool run = (fmap exitCodeToBool) . system main = liftM2 (&&) (run "foo") (run "bar") 

But the "foo" command returns ExitFailure, and I expect that "bar" will never be run. This is not the case! They both start and both show errors on the console.

In the same time

 False && (all (/= 0) [1..]) 

excellently appreciated; this means that the second argument is not evaluated. How do I do the same with system commands in my application?

+5
source share
3 answers

I think using && for conditional execution is a bit of a bad habit. Of course, it's just a matter of doing this for side effects such as False && all (/=0) [1..] , but when there are side effects, it's pretty confusing to make them dependent in such a hidden way. (Since the practice is so widespread, most programmers will recognize it right away, but I donโ€™t think that this is what we should encourage, at least not in Haskell.)

What you want is a way to express: "perform some action until you get False ."

For your simple example, I will just do it explicitly:

 main = do e0 <- run "foo" when e0 $ run "bar" 

or short: run "foo" >>= (`when` run "bar") .

If you want to use it more widely, it is useful to do it in a more general way. Just checking the Boolean condition is not very general, you usually also want to pass some kind of result. Passing the results is the main reason why we use the monad for IO, and not just lists of primitive actions.

Yeah, monads! Indeed, you need the IO monad, but with the additional โ€œkill switchโ€: either you perform a sequence of actions, each of which may have some kind of result for the transfer, or - if any of them fails - you interrupt everything. Sounds the same as Maybe , right?

http://www.haskell.org/hoogle/?hoogle=MaybeT

 import Control.Monad.Trans.Maybe run :: String -> MaybeT IO () run s = MaybeT $ do e <- system s return $ if exitCodeToBool e then Just () else Nothing main = runMaybeT $ do run "foo" run "bar" 
+5
source

You say you want to run commands in parallel and run "bar" only if "foo" succeeds. It makes no sense. You must decide whether you want to run it in parallel or in series.

If you want to run "bar" only if "foo" succeeds, try:

 import System.Process main = do callCommand "foo" callCommand "bar" 

Or, if you want to run it in parallel, try:

 import System.Process import Control.Concurrent myForkIO :: IO () -> IO (MVar ()) myForkIO io = do mvar <- newEmptyMVar forkFinally io (\_ -> putMVar mvar ()) return mvar main = do mvar <- myForkIO $ callCommand "foo" callCommand "bar" takeMVar mvar 
+2
source

IO action

 liftM2 f action1 action2 

runs both actions for any binary function f (e.g. (&&) in your case). If you want to just run action1 , you can program it like this:

 --| Short-circuit && sAnd :: IO Bool -> IO Bool -> IO Bool sAnd action1 action2 = do b <- action1 if b then action2 else return False 

Use it as sAnd action1 action2 .

+1
source

Source: https://habr.com/ru/post/978587/


All Articles