我正在使用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
实例。
库作者是否有特殊原因尚未为Monad
派生ExitCode
实例,或者是否有更好的方法来对Haskell shell代码进行错误处理?
答案 0 :(得分: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)
顺便说一下,值得注意的是{em> turtle but rather by base, in the System.Exit
module未定义ExitCode
。
答案 1 :(得分:2)
ExitCode
不是monad,也不是monad变换器。 monad需要采用类型参数,monad变换器需要采用两个。 ExitCode
没有。现在假设我们忽略了那个不那么小的问题。
join :: ExitCode (ExitCode a) -> ExitCode a
是的,我也不能。您可以合理地争辩shell
应该生成Either FailureCode ()
,或者可能在ExceptT FailureCode IO
中工作,但图书馆的作者可能认为这个工作太混乱或不灵活。
答案 2 :(得分: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