我有一个程序可以解析程序的输出行,并生成一些输出数据结构,其中包含从这些行中提取的一些信息。为此,我正在使用turtle
:
import Turtle
import qualified Control.Foldl as F
import Control.Monad.Except
-- Just for illustration purposes
newtype Result a = Result [a]
type Error = String
collectOutput :: (MonadIO m, MonadError Error m) => Text -> m Result
collectOutput cmd = Result <$> fold (runCmd cmd) F.list
-- Yes, I know turtle does also streaming, but I need to return all the results at the same time. Sorry...
runCmd ::Text -> Shell Result
runCmd cmd = do
line <- inproc cmd [] empty
tryParseOutput
现在问题来了,当试图定义tryParseOutput
. inproc cmd [] empty
一旦发生错误,我想中止命令(由 生成)。为此,我看到的唯一方法是使用该die
函数,该函数将抛出IOError
:
tryParseOutput line =
case parseOutput line of
Left parseErr -> die $ show parseErr
Right res -> return res
但是现在这意味着我需要修改collectOutput
来处理IOExceptions
:
collectOutput :: (MonadIO m, MonadError Error m) => Text -> m Result
collectOutput cmd = do
mRes <- liftIO $ collectOutputIO cmd
case mRes of
Left err -> throwError err
Right res -> return res
collectOutputIO :: Text -> IO (Either Error Result)
collectOutputIO cmd =
(Right . Result <$> fold (runCmd cmd) F.list) `catch` handler
where
handler :: IOException -> Either Error Result
handler ex = show ex
虽然这可行,但我不喜欢我为解析错误抛出 IO 异常,并且涉及一些异常捕获和案例分析。所以我想知道这是否可以以更优雅的方式(可能更turtle
惯用)来解决,或者我应该忘记使用MonadError
和接受shell 脚本IOError
的生活事实?turtle