5

我正在尝试在 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..])

评价很好;这意味着不计算第二个参数。如何在我的应用程序中对系统命令执行相同操作?

4

3 回答 3

5

我认为使用&&条件执行是一个坏习惯。当然,对于False && all (/=0) [1..]. (因为这种做法非常普遍,大多数程序员会立即认出它;但我认为我们不应该鼓励这种做法,至少在 Haskell 中是这样。)

你想要的是一种表达方式:“执行一些动作,直到产生False”。

对于您的简单示例,我只是明确地这样做:

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

或简称:run "foo" >>= (`when` run "bar").

如果您想更广泛地使用它,最好以更通用的方式进行。简单地检查一个布尔条件不是很普遍,你通常还想传递某种结果。传递结果是我们为 IO 使用 monad 的主要原因,而不是简单的原始操作列表。

啊哈,单子!实际上,您需要的是 IO monad,但需要一个额外的“终止开关”:要么执行一系列操作,每个操作都可能传递一些结果,要么 - 如果其中任何一个失败 - 中止整个操作。听起来很像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"
于 2014-04-11T13:40:25.327 回答
2

您说您想并行运行命令并仅在“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
于 2014-04-11T13:36:58.123 回答
1

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.

于 2014-04-11T22:54:12.733 回答