Frage

Ich versuche MonadError zusammen mit Parsec zu verwenden. Ich habe kommen mit dem folgenden Code-Snippet:

f5 = do
    char 'a'
    throwError "SomeError"

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

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

Allerdings ist ret Left "SomeError", so scheint es die catchError hat keine Wirkung. Was ist der richtige Weg MonadError verwenden hier?

Ich würde es vorziehen MonadError über Parsec eigene Fehlerbehandlung zu verwenden, wie zum Beispiel, wenn ich:

try (many1 parser1) <|> parser2

Wenn Parser1 hier ausfällt, parser2 wird weitergehen, aber ich möchte eine Ausnahme haben, die völlig die Analyse bricht.

War es hilfreich?

Lösung

Ich habe den Eindruck, dass Sie versuchen, MonadError aus dem falschen Grund einzubeziehen.

Im try (many1 parser1) <|> parser2, das Verhalten, das Sie zu vermeiden, sind versucht, ergibt sich aus der Verwendung von try und <|> - wenn Sie nicht wie es zu tun, verschiedene combinators verwenden. Vielleicht wäre ein Ausdruck wie (many1 parser1) >> parser2 besser für Sie arbeiten? (Diese verwirft die Ergebnisse aus (many1 parser1);. Sie könnte natürlich Verwendung >>= und kombinieren die Ergebnisse aus (many1 parser1) mit denen aus parser2)


(Hinweis: Unter diesem Punkt gibt es keine wirklich gute Lösung für das Problem auf der Hand, nur ein paar Gedanken darüber, warum wohl einige Dinge nicht funktionieren ... Hoffentlich (etwas sein können) zu erleuchten, aber nicht zu viel erwarten.)

Eine nähere Untersuchung der Parallelabschnitts / MonadError Interaktion. Ich fürchte, es ist eine schmutzige Bit und ich bin immer noch nicht sicher, wie man am besten zu gehen, um zu tun, was der OP tun will, aber ich hoffe, die folgenden zumindest einen Einblick in die Gründe für den Mangel an Erfolg bieten der ursprüngliche Ansatz.

Zum einen Hinweis, dass ist es nicht richtig zu sagen, dass Parsec ist eine Instanz MonadError . Parsec ist der durch Parallelabschnitt erzeugte monadisch, wenn die innere monadisch Identität ist; Parallelabschnitt erzeugt Instanzen von MonadError, wenn und nur wenn es eine innere Monade gegeben wird, die mit sich selbst eine Instanz von MonadError zur Arbeit ist. Relevante Fragmente von GHCi Wechselwirkungen:

> :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)

Als nächstes wir haben uns ein funktionierendes Beispiel mit catchError und Parallelabschnitt . Betrachten Sie diese GHCi Interaktion:

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

Die Typanmerkung erscheint notwendig (dies intuitiv Sinn für mich zu machen scheint, aber es ist nicht auf die ursprüngliche Frage relevant, so dass ich nicht zu erarbeiten werde versuchen). Die Art des gesamten Ausdrucks wird durch GHC bestimmt wie folgt:

Either String (Either ParseError Char)

So haben wir ein regelmäßiges Parse Ergebnis bekommen - Either ParseError Char - eingewickelt in der Either String Monade anstelle der üblichen Identity Monade. Da Either String eine Instanz von MonadError ist, können wir throwError / catchError, aber der Handler übergeben catchError muss natürlich Wert des richtigen Typs produzieren verwenden. Das ist nicht sehr nützlich für die Analyseroutine bricht aus, ich habe Angst.

Zurück zum Beispiel Code aus der Frage. Das macht eine etwas andere Sache. Betrachten wir die Art der ret wie in der Frage definiert:

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

(Nach GHCi ... beachten Sie, dass ich die Monomorphie Einschränkung mit {-# LANGUAGE NoMonomorphismRestriction #-} zu heben hatte, ohne Typenannotationen Kompilieren Sie den Code zu haben.)

Dieser Typ ist ein Hinweis auf die Möglichkeit, etwas mit ret amüsant zu tun. Los geht's:

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

Im Nachhinein die Handler catchError gegeben erzeugen einen Wert unexpected verwenden, so natürlich, es wird sein (verwendbar als) ein Parser ... Und ich fürchte, ich sehe nicht, wie dies hämmern in etwas Nützliches zum Ausbrechen des Analyseprozesses.

Andere Tipps

Wenn Sie einen Parser zu beheben zu debuggen sind versucht, es ist wahrscheinlich einfacher zu bedienen error, Debug.Trace oder Dingsbums.

Auf der anderen Seite, wenn Sie die Analyse auf einigen Eingaben als Teil Ihres aktuellen Programm beenden müssen, aber es tut nicht so wegen eines try (...) <|> Konstrukts, dann haben Sie ein Fehler in der Logik und Sie sollten Ihre Grammatik, anstatt Hack um es mit der Fehlerbehandlung zu stoppen und zu überdenken.

Wenn Sie die Parser wollen auf einem bestimmten Eingang einen Teil der Zeit zu beenden, andere aber nicht, dann ist entweder etwas aus Ihrem Eingangsstrom fehlt (und soll hinzugefügt werden) oder ein Parser ist nicht die Lösung für Ihr Problem.

Wenn Sie die Parser wollen anmutig erholen sie von nicht-tödlichen Fehlern und zu halten, wenn möglich versuchen, aber mit einem Fehler beendet, wenn es nicht weitergehen kann, dann wollen Sie ... können etwas anderes als Parsec betrachten, weil es wirklich nicht konzipiert. Ich glaube, Utrecht University Haskell Parser combinator Bibliothek unterstützt diese Art von Logik viel leichter.

Bearbeiten : Soweit Parsec selbst ist eine Instanz von MonadError geht - ja, und seine eigene Fehlerbehandlung subsumiert, dass Funktionalität. Was Sie versuchen zu tun ist, Stapel ein zweiten Fehler Monade auf der Parsec, und Sie sind wahrscheinlich Probleme, weil es in der Regel umständlich ist zwischen Monade Transformatoren zu unterscheiden, die „redundant“ in dieser Art und Weise sind. Der Umgang mit mehreren Staaten Monaden ist berühmt umständlich, weshalb Parsec (ein Staat als auch Monade) bietet Funktionalität zu halten, benutzerdefinierten Zustand.

Mit anderen Worten, Parsec ein Fehler Monade ist nicht, dass Sie überhaupt nicht helfen, und in der Tat relevant ist zumeist im Sinne der Herstellung Ihres Problems erschwert.

, wenn Sie benötigen die Analyse auf einige Eingaben als Teil Ihres aktuellen Programms zu beenden, aber es tut nicht so, weil der Versuch (...) <|> Konstrukt, dann haben Sie einen Fehler in der Logik und Sie sollten Ihre Grammatik zu stoppen und zu überdenken, anstatt Hack um es mit Fehlerbehandlung.

Wenn Sie den Parser auf einem bestimmten Eingang einen Teil der Zeit zu beenden, andere aber nicht, dann ist entweder etwas aus Ihrem Eingangsstrom fehlt (und soll hinzugefügt werden) oder ein Parser ist nicht die Lösung Ihres Problem dar.

Diese Antwort beruht auf der Annahme, dass das Problem liegt in der Grammatik. Aber wenn ich die Grammatik bin mit einem Compiler zu füttern, gibt es andere Fehler, die eine Grammatik nicht umgehen kann. Nehmen wir an, eine variable Referenz auf eine Variable, die nicht definiert wurde. Und die Sprache als ein einziger Durchlauf angegeben, und Variablen ausgewertet werden als angetroffen. Dann wird die Grammatik gut. Die Analyse ist gut so. Aber als Ergebnis der Bewertung, was in der Grammatik angegeben wurde ein Fehler aufgetreten ist, die bestehende „nicht bestanden“ oder „unerwartet“ oder unzureichend mit diesem Problem zu befassen. Es wäre schön, ein Mittel zu haben, die Analyse abzubrechen, ohne zu hochgradiger Fehlerbehandlung greifen zu müssen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top