我的印象是你试图让 MonadError 参与进来是出于错误的原因。
在try (many1 parser1) <|> parser2 中,您试图避免的行为源于对try 和<|> 的使用——如果您不喜欢它,请使用不同的组合符。也许像(many1 parser1) >> parser2 这样的表达式更适合您? (这会丢弃来自(many1 parser1) 的结果;您当然可以使用>>= 并将来自(many1 parser1) 的结果与来自parser2 的结果结合起来。)
(注意:在这一点之下,手头的问题没有真正好的解决方案,只是对为什么有些事情可能不起作用的一些思考......希望这可能(有点)启发性,但不要期望太多。)
仔细检查 ParsecT / MonadError 交互。恐怕这有点混乱,我仍然不确定如何最好地去做 OP 想做的事情,但我希望以下内容至少可以深入了解缺乏成功的原因原来的方法。
首先,请注意说 Parsec 是 MonadError 的一个实例是不正确的。 Parsec 是当内部单子是 Identity 时 ParsecT 产生的单子; ParsecT 产生 MonadError 的实例当且仅当它被赋予一个内部 monad 时,它本身就是一个 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 monad 代替了通常的Identity monad。由于Either String 是MonadError 的一个实例,我们可以使用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 生成一个值,所以它当然会(用作)解析器......而且我恐怕我不知道如何锤击这对打破解析过程很有用。