在Turtle中编写ExitCodes。为什么没有Monad / Monad Transformer实例?

时间:2015-10-19 23:23:24

标签: haskell haskell-turtle

我正在使用turtle在Haskell中编写一个shell脚本,并希望了解编写可能失败的命令的最佳实践。

现在我有一个案例表达式楼梯,如下:

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实例。

库作者是否有特殊原因尚未为Monad派生ExitCode实例,或者是否有更好的方法来对Haskell shell代码进行错误处理?

3 个答案:

答案 0 :(得分:5)

另一种方法是使用(.&&.) and (.||.) from Turtle.Prelude

  

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

     

类似于Bash中的&&

     

仅当第一个命令返回ExitSuccess

时才运行第二个命令
  

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

     

类似于Bash中的||

     

仅当第一个命令返回ExitFailure

时才运行第二个命令

它们允许您像这样链接命令(请注意,所涉及的所有内容都必须返回ExitCode,包括清理):

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

或者,如果您在每种情况下都需要不同的清理操作:

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

顺便说一下,值得注意的是{em> turtle but rather by base, in the System.Exit module未定义ExitCode

答案 1 :(得分:2)

ExitCode不是monad,也不是monad变换器。 monad需要采用类型参数,monad变换器需要采用两个。 ExitCode没有。现在假设我们忽略了那个不那么小的问题。

你能想出一个有意义的解释吗?
join :: ExitCode (ExitCode a) -> ExitCode a

是的,我也不能。您可以合理地争辩shell应该生成Either FailureCode (),或者可能在ExceptT FailureCode IO中工作,但图书馆的作者可能认为这个工作太混乱或不灵活。

答案 2 :(得分:1)

您可以使用MaybeT来避免以这种方式进行楼梯:

{-# 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