Составление кодов выхода в 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