2015-10-19 41 views
6

我正在使用turtle在Haskell中編寫一個shell腳本,並希望瞭解有關編寫可能失敗的命令的最佳實踐。在海龜中編寫ExitCodes。爲什麼沒有Monad/Monad Transformer實例?

現在我有一個CASE表達式的樓梯,像這樣:

runRemote :: MonadIO io => Text -> Text -> io() 
runRemote oldVersion' newVersion' = sh $ do 
    mkdir "out" 
    e1 <- shell ("command " <> oldVersion') empty 
    case e1 of 
    ExitFailure n -> cleanup 
    ExitSuccess -> do 
     e2 <- shell ("command " <> newVersion') empty 
     case e2 of 
     ExitFailure n -> cleanup 
     ExitSuccess -> do 
      curDir <- pwd 
      cd (curDir <.> oldVersion') 
      e3 <- shell ("command something else") empty 
      case e3 of 
      -- ... 
      -- And so on... 

如果case表達對Maybe型擴,該解決方案將得到一個Monad實例。

是否有一個特殊的原因,庫作者還沒有爲ExitCode派生出一個Monad實例,還是有更好的方法來處理Haskell shell代碼的錯誤?

+3

由於ExitCode具有kind *類型,而Monad類型需要類型爲* - > *'的類型,因此不可能爲ExitCode創建一個Monad實例。類型參數)。 –

+1

您可能會喜歡[我如何處理許多級別的縮進?](http://stackoverflow.com/q/33005903/791604)。 –

回答

5

一種選擇是使用(.&&.) and (.||.) from Turtle.Prelude

(.&&.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode

類似於&&在擊

運行僅當第一個返回ExitSuccess

(.||.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode

第二命令

類似於||在猛砸

運行僅當第一個返回ExitFailure

它們允許您鏈中的第二個命令你的命令,就像這樣(請注意,所有有關各方必須返回一個ExitCode,包括清理):

(command1 .&&. command2) .||. cleanup 

或者,如果您需要在各種情況下不同的清理操作:

(command1 .||. cleanup1) .&&. (command2 .||. cleanup2) 

順便說一句,值得注意的是ExitCode不是由龜定義的but rather by base, in the System.Exit module

2

ExitCode不是一個單子,而不是一個單子轉換。 monad需要採用類型參數,monad變換器需要採用兩個參數。 ExitCode不需要。現在假設我們稍微忽略了這個不那麼小的問題。你能想出一個有意義的解釋:

join :: ExitCode (ExitCode a) -> ExitCode a 

是的,我不能。您可以合理辯解,shell應該生成Either FailureCode(),或者可能在ExceptT FailureCode IO中工作,但圖書館作者可能認爲該作業過於混亂或不靈活。

1

您可以使用MaybeT避免staircasing這樣:

{-# LANGUAGE OverloadedStrings #-} 

import Turtle 
import Control.Monad.Trans 
import Control.Monad.Trans.Maybe 

check io = do ec <- lift io 
       MaybeT $ case ec of 
         ExitSuccess -> return (Just True) 
         _   -> return Nothing 

checkShell a b = check (shell a b) 

main = do 
    dostuff 
    putStrLn "cleaning up" 

dostuff = runMaybeT $ do 
    checkShell "date" empty 
    checkShell "/usr/bin/false" empty 
    checkShell "pwd" empty