我的印象是您MonadError
出于错误的原因试图参与其中。
在 中try (many1 parser1) <|> parser2
,您试图避免的行为源于使用try
and <|>
-- 如果您不喜欢它,请使用不同的组合符。也许像(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
,所以它当然会(用作)一个解析器......而且我担心我不知道如何将它锤炼成一些有用的东西来打破解析过程。