IOモナドとの論理的および厳密さ
-
21-12-2019 - |
質問
Haskellで簡単なプログラムを書いています。基本的に2つのシェルコマンドを並列に実行する必要があります。これがコード:
です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..])
.
は完全によく評価されます。これは、2番目の引数が計算されないことを意味します。私のアプリのシステムコマンドと同じことをどうやってもいいですか?
解決
条件付き実行のために&&
を使用することは悪い習慣のものです。これは、副作用のないのようなもののためにこれを行う理由の問題のほんの問題の問題ですが、副作用がある場合は、そのような隠れた方法で依存するのはかなり軽蔑です。 (練習がとても広くなるため、ほとんどのプログラマーはすぐに認識します。しかし、それが私たちが励ますべきことだとは思わない。)
あなたが望むものは表現する方法です: "何らかの操作を実行する、がFalse && all (/=0) [1..]
を生成するまでを実行します"。
あなたの簡単な例では、明示的にそれをするでしょう:
main = do
e0 <- run "foo"
when e0 $ run "bar"
.
またはshort:False
。
これをより広範囲に使用したい場合は、より一般的な方法でそれをするのが良いです。単にブール状態をチェックすることは一般的ではありませんが、通常はある種の結果を渡したいと思うでしょう。結果を過ぎるのは、単にプリミティブアクションのリストではなく、IOのためにモナドを使用する主な理由です。
AHA、モナド!確かに、あなたが必要なものはIO MONADですが、追加の「キルスイッチ」を使用して:それぞれの行動を実行します。それぞれの結果を渡すことができますか、または - それらのいずれかが失敗した場合 - 全体を中止します。 run "foo" >>= (`when` run "bar")
、右?
http://www.haskell.org/hoogle/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
.