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

Composing ExitCodes in Turtle. Why is there no Monad/Monad Transformer instance?

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

现在我有一个 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 [=29 进行错误处理=]代码?

ExitCode 不是 monad,也不是 monad 转换器。一个 monad 需要接受一个类型参数,一个 monad transformer 需要接受两个。 ExitCode 占用 none。现在假设我们暂时忽略这个不那么小的问题。你能想出一个有意义的解释吗

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

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

您可以使用 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

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

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

Analogous to && in Bash

Runs the second command only if the first one returns ExitSuccess

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

Analogous to || in Bash

Run the second command only if the first one returns ExitFailure

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

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

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

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

顺便说一下,值得注意的是ExitCode不是由turtlebut rather by base, in the System.Exit module.

定义的