我试图用秒差距使用MonadError在一起。我想出了下面的代码片段:

f5 = do
    char 'a'
    throwError "SomeError"

f6 = f5 `catchError` (\e -> unexpected $ "Got the error: " ++ e)

ret = runErrorT (runParserT f6 () "stdin" "a")

然而,retLeft "SomeError",看来catchError不具有任何影响。什么是这里使用MonadError的正确方法?

我宁愿使用MonadError超过秒差距自身的错误处理,例如当我有:

try (many1 parser1) <|> parser2

如果parser1这里失败,parser2将继续,但我想有这完全中止解析异常。

有帮助吗?

解决方案

我的印象是,你要涉及对错误的原因MonadError下。

try (many1 parser1) <|> parser2,你试图避免的行为从使用try<|>的茎 - 如果你不喜欢它,使用不同的组合子。也许像(many1 parser1) >> parser2的表达会为你工作好? (此丢弃从(many1 parser1)结果;你当然可以使用>>=的,结果从与那些从(many1 parser1) parser2结合)


(注:以下这一点上,没有真正很好地解决手头的问题,只是一些沉思,为什么有些事情可能不会工作...希望这可能是(有点)振聋发聩,但不要指望太多。)

一个ParsecT / MonadError相互作用的仔细检查。恐怕这是一个有点乱,我还是真的不知道如何最好地去这样做的OP想要做什么,但我希望以下至少会提供深入了解的原因,缺乏成功的原的方法。

首先,注意的它是不正确的说,是秒差距MonadError 的实例。秒差距是由ParsecT产生的单子当内单子是身份;当且仅当它被赋予一个内单子其本身MonadError的与工作实例ParsecT产生MonadError的实例。 GHCI相互作用的相关片段:

> :i Parsec
type Parsec s u = ParsecT s u Identity
    -- Defined in Text.Parsec.Prim
-- no MonadError instance

instance (MonadError e m) => MonadError e (ParsecT s u m)
  -- Defined in Text.Parsec.Prim
-- this explains why the above is the case
-- (a ParsecT-created monad will only become an instance of MonadError through
-- this instance, unless of course the user provides a custom declaration)

接着,的让我们赞同catchError和ParsecT 的工作示例。考虑这个GHCI相互作用:

> (runParserT (char 'a' >> throwError "some error") () "asdf" "a" :: Either String (Either ParseError Char)) `catchError` (\e -> Right . Right $ 'z')
Right (Right 'z')

类型标注似乎有必要(这似乎很直观的感觉对我来说,却是不相关的原来的问题,所以我不会试图阐述)。类型整个表达式由GHC确定为如下:

Either String (Either ParseError Char)

所以,我们已经有了一个常规的解析结果 - Either ParseError Char - 裹着Either String单子代替通常Identity单子。由于Either StringMonadError的一个实例,我们可以使用throwError / catchError,但处理程序传递给catchError必须的过程产生正确类型的值。这不是对分析例程的突破是非常有用的,我害怕。

返回从问题的示例代码。这的确稍微不同的事情。让我们来看看ret的类型,在问题定义:

forall (m :: * -> *) a.
(Monad m) =>
m (Either [Char] (Either ParseError a))

(根据GHCI ...注意,我必须解除单态限制与{-# LANGUAGE NoMonomorphismRestriction #-}具有不类型的注释的代码编译。)

这类型是提供提示以做一些与ret会哄的可能性。在这里,我们去:

> runParserT ret () "asdf" "a"
Right (Left "some error")

在事后,给catchError处理程序产生使用unexpected的值,所以当然这将是(可作为)一个解析器......我怕我不知道如何锤成有用的东西这对于解析过程的爆发。

其他提示

如果你想调试分析器来解决,它可能更易于使用errorDebug.Trace,或诸如此类的东西。

在另一方面,如果你需要终止某些输入解析为您的实际计划的一部分,但它没有这样做,因为try (...) <|>结构的话,那么你有一个的的错误逻辑中的你应该停下来和错误处理重新思考你的语法,而不是黑客绕过它。

如果您想解析器终止对给定输入一些时间,但不是别人,那么无论东西是从输入流中缺失的(应添加)或解析器是不是解决你的问题。

如果您想解析器从非致命错误正常恢复,并继续努力在可能的情况,但是当它不能继续错误终止,那么你......可能要考虑比其他秒差距的东西,因为它是真的不专为。我相信,乌得勒支大学的Haskell解析器组合库支持那种逻辑更加容易。

修改:据秒差距存在本身MonadError的实例老话 - 是的,和自己的错误处理涵括该功能。你试图做的是堆栈的第二的上秒差距的最严重的错误单子,你很可能遇到麻烦,因为通常尴尬单子上变压器,在这种方式“冗余”区别开来。与多个国家单子处理是更著名别扭,这就是为什么秒差距(状态单子以及)提供的功能,以保持自定义状态。

在换句话说,秒差距是一个错误的单子不帮你在所有的,而事实上是相关的主要是使你的问题更困难的感觉。

如果你需要终止某些输入解析为您的实际计划的一部分,但它没有这样做,因为一试,这样(...)<|>构造,那么你必须在你的逻辑错误你应该停下来重新考虑你的语法,而不是周围的黑客与错误处理。

如果你想解析器终止对给定输入一些时间,但不是别人,那么无论东西是从输入流中缺失的(应添加)或解析器是不是解决您的问题。

这答案是基于这样的假设,问题出在语法。但是,如果我使用的语法养活一个编译器,还有其他的错误,语法不能处理。比方说,一个变量引用,没有定义一个变量。和语言被指定为一个单一的传球,并且遇到变量进行评估。然后,语法就好了。解析就好了。但作为评估什么是在指定的语法错误已经发生的结果,现有的“失败”或“意外”或不足以解决这个问题。这将是很好有一个手段来中止解析不诉诸更高层次的错误处理。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top