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

6
我正在使用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实例。
库作者没有为ExitCode派生一个Monad实例是否有特殊的原因,或者有更好的方法来处理Haskell shell代码的错误处理?

3
无法为 ExitCode 创建一个 Monad 实例,因为 ExitCode 的 kind 是 *,而 Monad 类型类需要一个 kind 为 * -> * 的类型(即具有类型参数的类型)。 - David Young
1
你可能会喜欢如何处理多层缩进? - Daniel Wagner
3个回答

5
一种替代方案是使用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)

顺便提一下,值得注意的是,ExitCode 不是由 turtle 定义的,而是由baseSystem.Exit模块中定义


2

ExitCode不是一个单子,也不是一个单子变换器。单子需要带有类型参数,而单子变换器需要带有两个。ExitCode 不需要任何参数。现在假设我们暂时忽略这个不太小的问题,你能想出一个有意义的解释吗?

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

是的,我也看不懂。你可以合理地认为shell应该生成Either FailureCode (),或者使用ExceptT FailureCode IO,但库的作者可能认为这对工作来说太令人困惑或不灵活了。


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接