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
从解析器到解释器的结果,同时保释并打印可能的 ParseError
或 IntrpError
.
最接近我想要的是这样的
main = do
fname <- getArgs
input <- readFile (head fname)
let prog = lparse (head fname) input
(putStrLn . show) (intrp <$> prog)
但这不会令人惊讶地产生嵌套的 Either 并且打印效果也不佳。
那么,是否有任何很好的Haskell ideomatic 方法来完成从一个计算到另一个计算的线程化结果并以一种没有嵌套情况的好方法处理错误 (Lefts
)?
编辑
添加类型 lparse
和 itrp
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
我重温了 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
从解析器到解释器的结果,同时保释并打印可能的 ParseError
或 IntrpError
.
最接近我想要的是这样的
main = do
fname <- getArgs
input <- readFile (head fname)
let prog = lparse (head fname) input
(putStrLn . show) (intrp <$> prog)
但这不会令人惊讶地产生嵌套的 Either 并且打印效果也不佳。
那么,是否有任何很好的Haskell ideomatic 方法来完成从一个计算到另一个计算的线程化结果并以一种没有嵌套情况的好方法处理错误 (Lefts
)?
编辑
添加类型 lparse
和 itrp
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