У меня сложилось впечатление, что вы пытаетесь привлечь MonadError
по неправильной причине.
В try (many1 parser1) <|> parser2
поведение, которое вы пытаетесь избежать, связано с использованием try
и <|>
- если вам это не нравится, используйте разные комбинаторы. Возможно, выражение типа (many1 parser1) >> parser2
подойдет вам лучше? (Это отбрасывает результаты из (many1 parser1)
; вы, конечно, можете использовать >>=
и комбинировать результаты из (many1 parser1)
с результатами из parser2
.)
(Примечание: ниже этого пункта нет действительно хорошего решения рассматриваемой проблемы, только некоторые размышления о том, почему некоторые вещи, вероятно, не будут работать ... Надеюсь, это может быть (несколько) поучительно, но не ожидайте слишком многого.)
Более детальное изучение взаимодействия ParsecT / MonadError. Я боюсь, что это немного грязно, и я все еще не совсем уверен, как лучше делать то, что хочет сделать ОП, но я надеюсь, что следующее, по крайней мере, даст представление о причинах отсутствия успеха оригинальный подход.
Во-первых, обратите внимание, что неверно говорить, что Parsec является экземпляром MonadError . Parsec - это монада, созданная ParsecT, когда внутренняя монада - это Идентичность; ParsecT создает экземпляры MonadError тогда и только тогда, когда ему присваивается внутренняя монада, которая сама является экземпляром 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 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
, поэтому, конечно, это будет (можно использовать как) синтаксический анализатор ... И я боюсь, что не вижу, как забить это полезно для выхода из процесса синтаксического анализа.