Составление кодов выхода в Turtle. Почему нет экземпляра Monad/Monad Transformer?

Я пишу сценарий оболочки в Haskell, используя turtle и хотел бы узнать лучшие практики по составлению команд, которые могут потерпеть неудачу.

Теперь у меня есть лестница с выражением падежа, вот так:

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?

3 ответа

Решение

Одна альтернатива использует (.&&.) а также (.||.) от 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 определяется не черепахой, а скорее основанием, в System.Exit модуль.

ExitCode это не монада и не монадный трансформатор. Монада должна принимать аргумент типа, а преобразователь монады должен принимать два. ExitCode не берет ни одного Теперь предположим, что мы немного игнорируем эту не так уж небольшую проблему. Можете ли вы придумать значимое толкование

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
Другие вопросы по тегам