在 Turtle 中编写 ExitCodes。为什么没有 Monad/Monad 个 Transformer 实例?
Composing ExitCodes in Turtle. Why is there no Monad/Monad Transformer instance?
我正在使用 turtle
在 Haskell 中编写一个 shell 脚本,并且想了解有关编写可能会失败的命令的最佳实践。
现在我有一个 case 表达式阶梯,像这样:
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 [=29 进行错误处理=]代码?
ExitCode
不是 monad,也不是 monad 转换器。一个 monad 需要接受一个类型参数,一个 monad transformer 需要接受两个。 ExitCode
占用 none。现在假设我们暂时忽略这个不那么小的问题。你能想出一个有意义的解释吗
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
另一种方法是使用 (.&&.)
and (.||.)
from Turtle.Prelude
.
(.&&.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode
Analogous to &&
in Bash
Runs the second command only if the first one returns ExitSuccess
(.||.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode
Analogous to ||
in Bash
Run the second command only if the first one returns ExitFailure
它们允许您像这样链接命令(请注意,涉及的所有内容都必须 return 和 ExitCode
,包括清理):
(command1 .&&. command2) .||. cleanup
或者,如果您在每种情况下需要不同的清理操作:
(command1 .||. cleanup1) .&&. (command2 .||. cleanup2)
顺便说一下,值得注意的是ExitCode
不是由turtlebut rather by base, in the System.Exit
module.
定义的
我正在使用 turtle
在 Haskell 中编写一个 shell 脚本,并且想了解有关编写可能会失败的命令的最佳实践。
现在我有一个 case 表达式阶梯,像这样:
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 [=29 进行错误处理=]代码?
ExitCode
不是 monad,也不是 monad 转换器。一个 monad 需要接受一个类型参数,一个 monad transformer 需要接受两个。 ExitCode
占用 none。现在假设我们暂时忽略这个不那么小的问题。你能想出一个有意义的解释吗
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
另一种方法是使用 (.&&.)
and (.||.)
from Turtle.Prelude
.
(.&&.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode
Analogous to
&&
in BashRuns the second command only if the first one returns
ExitSuccess
(.||.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode
Analogous to
||
in BashRun the second command only if the first one returns
ExitFailure
它们允许您像这样链接命令(请注意,涉及的所有内容都必须 return 和 ExitCode
,包括清理):
(command1 .&&. command2) .||. cleanup
或者,如果您在每种情况下需要不同的清理操作:
(command1 .||. cleanup1) .&&. (command2 .||. cleanup2)
顺便说一下,值得注意的是ExitCode
不是由turtlebut rather by base, in the System.Exit
module.