Logical AND strictness with IO monad

不羁的心 提交于 2019-12-05 08:27:22

I think using && for conditional execution is something of a bad habit. Sure it's just a matter of reason to do this for side-effect-free stuff like the False && all (/=0) [1..], but when there are side-effects it's quite confusionsome to make them dependent in such a hidden way. (Because the practise is so widespread, most programmers will immediately recognise it; but I don't think it's something we should encourage, at least not in Haskell.)

What you want is a way to express: "execute some actions, until one yields False".

For your simple example, I'd 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 this more extensively, it's good to do it in a more general manner. Simply checking a boolean condition is not very general, you'll normally also want to pass on some kind of result. Passing on results is the main reason we use a monad for IO, rather then simply lists of primitive actions.

Aha, monads! Indeed, what you need is the IO monad, but with an extra "kill switch": either you do a sequence of actions, each possibly with some result to pass on, or – if any of them fails – you abort the entire thing. Sounds a lot like 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"

You say that you want to run commands in parallel and run "bar" only if "foo" succeeds. It makes no sense. You have to decide, if you want to run it in parallel or sequential.

If you want run "bar" only if "foo" succeed, 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

The IO action

liftM2 f action1 action2

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

--| 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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!