IO monad的逻辑和严格性
我正在尝试在Haskell中编写一个简单的程序。 它应该基本上并行运行两个shell命令。 代码如下:
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")
但命令“foo”返回ExitFailure,我希望“bar”永远不会运行。 不是这种情况! 它们都运行并且都在控制台上显示错误。
与此同时
False && (all (/= 0) [1..])
评估非常好; 这意味着第二个参数不计算。 我如何在我的应用程序中执行系统命令?
我认为使用&&
进行条件执行是一个坏习惯。 当然这只是一个合理的做法,可以避免副作用,例如False && all (/=0) [1..]
,但是当有副作用时,让它们依赖于这样的隐藏的方式。 (因为这种做法非常普遍,所以大多数程序员会立即认识到这一点;但我认为这不是我们应该鼓励的,至少在Haskell中是这样。)
你想要的是一种表达方式:“执行一些动作,直到产生False
”。
举个简单的例子,我只是明确地做到这一点:
main = do
e0 <- run "foo"
when e0 $ run "bar"
或简称: run "foo" >>= (`when` run "bar")
。
如果你想更广泛地使用它,最好是以更一般的方式来完成。 简单地检查一个布尔条件不是很一般,你通常也会想传递某种结果。 传递结果是我们使用monad来处理IO的主要原因,而不是简单地列出原始操作。
啊哈,monads! 事实上,你需要的是IO monad,但是有一个额外的“kill switch”:要么执行一系列操作,每个操作都可能会传递一些结果,或者 - 如果其中任何一个失败 - 则会中止整个事件。 听起来很像Maybe
,对吧?
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"
你说你想并行运行命令,只有在“foo”成功时才运行“bar”。 这个不成立。 你必须决定,如果你想要并行或顺序运行它。
如果您只想在“foo”成功时运行“bar”,请尝试:
import System.Process
main = do callCommand "foo"
callCommand "bar"
或者,如果您想要并行运行它,请尝试:
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
IO操作
liftM2 f action1 action2
为任何二进制函数运行f
(例如, (&&)
)。 如果你想运行action1
你可以按如下方式编写它:
--| Short-circuit &&
sAnd :: IO Bool -> IO Bool -> IO Bool
sAnd action1 action2 = do
b <- action1
if b then action2 else return False
将其用作sAnd action1 action2
。
上一篇: Logical AND strictness with IO monad
下一篇: What does "coalgebra" mean in the context of programming?