【问题标题】:Composing ExitCodes in Turtle. Why is there no Monad/Monad Transformer instance?在 Turtle 中编写 ExitCodes。为什么没有 Monad/Monad Transformer 实例?
【发布时间】:2016-01-18 11:00:30
【问题描述】:

我正在使用 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 代码的错误?

【问题讨论】:

  • 不可能为ExitCode 创建一个Monad 实例,因为ExitCode 有一种* 并且Monad 类型类需要一个类型为* -&gt; * 的类型(一种有一个类型参数)。

标签: haskell haskell-turtle


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      (.&amp;&amp;.) :: Monad m =&gt; m ExitCode -&gt; m ExitCode -&gt; m ExitCode

      类似于 Bash 中的 &amp;&amp;

      仅当第一个命令返回 ExitSuccess 时才运行第二个命令

      (.||.) :: Monad m =&gt; m ExitCode -&gt; m ExitCode -&gt; m ExitCode

      类似于 Bash 中的 ||

      仅当第一个命令返回 ExitFailure 时才运行第二个命令

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

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

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

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

      顺便提一下,ExitCode 不是由 turtle but rather by base, in the System.Exit module 定义的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-01
        • 2017-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多