2014-04-11 19 views
5

Haskell'de basit bir program yazmaya çalışıyorum. Temel olarak iki kabuk komutunu paralel olarak çalıştırmalıdır.IO monad ile Mantıksal ve Kesinlik

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") 

Ama komutu "foo" ExitFailure döner ve ben çalıştırmak için asla "bar" bekliyoruz: İşte kodudur. Durum bu değil! Her ikisi de çalışır ve her ikisi de konsolda hataları gösterir. Aynı zamanda

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

de

de mükemmel değerlendirir Bu ikinci argüman hesaplanmamış demektir. Uygulamamdaki sistem komutlarıyla aynı şeyi nasıl yaparım?

+2

'liftM2' tanımına bakın. İstediğiniz (monadic) birliği olan '(&&)' nin monadik bir versiyonuna ihtiyacınız var. – augustss

+4

Komutları paralel olarak çalıştırmak ve sadece "foo" başarılı olursa "bar" ı çalıştırmak istediğinizi söylüyorsunuz. Hiç bir anlamı yok. Karar ver, paralel mi sıralı mı istiyorsun? – remdezx

cevap

5

ben && kullanarak düşünüyorum koşullu yürütme için kötü bir alışkanlıktır. Tabii ki, False && all (/=0) [1..] gibi gibi bir şeyler için yan etkisi için bunu yapmak için bir neden meselesi olduğundan emin olun, ancak yan etkileri olduğunda bunları gizli bir şekilde bağımlı hale getirmek oldukça karmaşıktır. (Uygulama çok yaygın olduğu için çoğu programcı bunu hemen tanıyacaktır, ama en azından Haskell'de değil, teşvik etmemiz gerektiğini düşünüyorum.)

İstediğiniz şey ifade etmenin bir yoludur: " eylemleri,'a kadar numaralı "False" değerini verir. senin Basit bir örnek için

, sadece açıkça yapardım:

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

veya kısa: run "foo" >>= (`when` run "bar").

Bunu daha kapsamlı kullanmak isterseniz, daha genel bir şekilde yapmak daha iyidir. Sadece bir boole durumunun kontrol edilmesi çok genel değildir, normalde bir tür sonuca geçmek istersiniz. Sonuçların üstesinden gelmek, IO için bir monad kullanmamızın temel sebebidir, daha sonra basitçe ilkel eylemlerin listesini içerir.

Aha, monads! İhtiyacınız olan şey, IO monad'tır, ancak ekstra bir "kill switch" ile: ya bir eylemler dizisi yaparsınız, her biri muhtemelen bir sonuca ulaşırsa, ya da - eğer bunlardan herhangi biri başarısız olursa, tüm şeyi iptal edersiniz. Maybe'a çok benziyor, değil mi?

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" 
2

Komutları paralel olarak çalıştırmak ve "foo" nun başarılı olması koşuluyla "bar" ı çalıştırmak istediğinizi söylüyorsunuz. Hiç bir anlamı yok. Paralel veya sıralı olarak çalıştırmak istiyorsanız, karar vermelisiniz.

import System.Process 

main = do callCommand "foo" 
      callCommand "bar" 

Yoksa paralel olarak çalıştırmak istiyorsanız, denemek:

siz "foo" başarılı yalnızca "bar" çalıştırmak istiyorsanız, denemek

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 
1

IO eylem

liftM2 f action1 action2 

çalışır herhangi ikili işlevi f olduğunu için her iki işlem (sizin durumunuzda (&&)).Sadece çalıştırmak isterseniz aşağıdaki gibi action1 bunu kodlayabiliriz:

--| 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 olarak kullanın.

İlgili konular