Haskell:处理计算结果 Either

Haskell: Handling resulting Either from computations

我重温了 Haskell lateley 并构建了一种玩具编程语言 parser/interpreter。使用 Parsec 进行词法分析和解析以及一个单独的解释器。我 运行 遇到了将解析器的结果提供给我的解释器并处理来自解释器和解析器的潜在错误的一些问题。我最终得到这样的结果:

main = do
  fname <- getArgs
  input <- readFile (head fname)
  case lparse (head fname) input of
    Left msg -> putStrLn $ show msg
    Right p -> case intrp p of
      Left msg -> putStrLn $ show msg
      Right r -> putStrLn $ show r

这看起来一点也不漂亮。我的问题是 lparse returns Either ParseError [(String, Stmt)]itrp returns 类型 Either ItrpError Stmt 所以我很难喂 Right 从解析器到解释器的结果,同时保释并打印可能的 ParseErrorIntrpError.

最接近我想要的是这样的

main = do
  fname <- getArgs
  input <- readFile (head fname)
  let prog = lparse (head fname) input
  (putStrLn . show) (intrp <$> prog)

但这不会令人惊讶地产生嵌套的 Either 并且打印效果也不佳。

那么,是否有任何很好的Haskell ideomatic 方法来完成从一个计算到另一个计算的线程化结果并以一种没有嵌套情况的好方法处理错误 (Lefts)?

编辑

添加类型 lparseitrp

lparse :: Text.Parsec.Pos.SourceName -> String -> Either Text.Parsec.Error.ParseError [([Char], Stmt)]

intrp :: [([Char], Stmt)] -> Either IntrpError Stmt

好吧,如果你想链接成功的计算,你总是可以使用 >>= 来做到这一点。例如你的情况:

lparse (head fname) input >>= intrp

如果你想打印出你的错误消息,你可以使用 either class ,它有两个处理函数,一个用于你有 Left a 的情况(错误在你的情况下)和另一个 Right b (在你的情况下是成功的事情)。一个例子:

either (putStrLn . show) (putStrLn . show) (lparse (head fname) input >>= intrp)

如果你的链中出现任何故障(你的 monadic 链的任何一步都变成 Left a),它会停止并可以打印出上述情况中的错误消息。

虽然不完美,但我会创建一个辅助函数,用于将来自 Either 的任何 Show 可能的错误嵌入到 MonadError:

{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.Except

strErr :: (MonadError String m, Show e) => Either e a -> m a
strErr = either (throwError . show) return

然后如果你有一个可能因错误而失败的计算,比如

someFn :: ExceptT String IO ()
someFn = strErr (Left 42)

你可以 运行 它(打印错误到 stdout)作为

main :: IO ()
main = runExceptT someFn >>= either putStrLn return

在你的情况下它会是这样的

main = either putStrLn return <=< runExceptT $ do
  fname <- liftIO getArgs
  input <- liftIO $ readFile (head fname)
  prog <- strErr $ lparse (head fname) input
  r <- strErr $ interp prog
  print r