Если вы хотите, чтобы монада вел себя по-другому, возможно, вам следует создать другую монаду. (Н.Б. Я не совсем понимаю, чего вы хотите, но все равно буду двигаться вперед).
Решение: используйте монадный стек трансформаторов
Например, чтобы получить fail
-подобную функцию, которая не перехватывается и не игнорируется парсекской try
, вы можете использовать Кроме монады . Except
позволяет генерировать ошибки, похожие на исключения, но они монтируются монадически вместо использования фактического механизма исключений, который требует IO для его обнаружения.
Во-первых, давайте определим нашу монаду:
import Text.Parsec
import Text.Parsec.Combinator
import Text.Parsec.Char
import Control.Monad.Trans.Except
import Control.Monad.Trans
type EscParse a = ParsecT String () (Except String) a
Итак, монада EscParse
и сочетает в себе функции Parsec (через преобразователь ParsecT
) и Except
.
Во-вторых, давайте определимся с некоторыми помощниками:
run :: EscParse a -> SourceName -> String -> Either String (Either ParseError a)
run op sn input = runExcept (runPT op () sn input)
escFail :: String -> EscParse a
escFail = lift. throwE
Наш run
похож на runParse
, но также запускает монаду кроме. Возможно, вы захотите сделать что-нибудь, чтобы избежать вложенного Either, но это легкое косметическое изменение. escFail
- это то, что вы используете, если не хотите, чтобы ошибка игнорировалась.
В-третьих, нам нужно реализовать ваш парсер с помощью этой новой монады:
parseA :: EscParse String
parseA = try seq1 <|> seq2
seq1 :: EscParse String
seq1 = do manyTill anyChar (try $ string "\n* ")
many1 anyChar
escFail "My error message"
seq2 :: EscParse String
seq2 = do manyTill anyChar (try $ string "\n- ")
many1 anyChar
Кроме пробелов и сигнатур типа, вышеприведенное соответствует тому, что вы имели, но с использованием escFail
вместо fail
.