2015-10-19 3 views
6

Я пишу сценарий оболочки в Haskell с помощью turtle и хотел бы знать лучшие практики составления команд, которые могут выйти из строя.Составление ExitCodes в Черепахе. Почему нет экземпляра Monad/Monad Transformer?

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

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

Невозможно создать экземпляр 'Monad' для' ExitCode', потому что 'ExitCode' имеет вид' * ', а класс типа' Monad' требует типа с видом '* -> *' (тип, который имеет аргумент типа). –

+1

Вам может понравиться [Как я могу справиться со многими уровнями отступов?] (Http://stackoverflow.com/q/33005903/791604). –

ответ

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) 

Кстати, стоит отметить, что ExitCode не указан черепахаbut rather by base, in the System.Exit module.

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 
Смежные вопросы