Frage

aus der Community Ocaml kommen, ich versuche ein wenig Haskell zu lernen. Der Übergang geht ganz gut, aber ich bin ein wenig verwirrt mit dem Debuggen. Früher habe ich setzen (viel) „printf“ in meinem ocaml Code, einige Zwischenwerte zu kontrollieren, oder als Flagge zu sehen, wo die Berechnung genau fehlgeschlagen.

Da printf ein ist IO Aktion, habe ich den gesamten Code meine Haskell heben innerhalb der IO Monade auf diese Art in der Lage sein Debugging? Oder gibt es einen besseren Weg, dies zu tun (ich wirklich nicht will, dass es von Hand zu tun, wenn es vermieden werden kann)

Ich finde auch die Spur Funktion: http://www.haskell.org/haskellwiki/Debugging#Printf_and_friends das scheint genau das, was ich will, aber ich verstehe nicht seine Art: Es gibt kein IO überall! Kann mir jemand das Verhalten der Trace-Funktion erklären?

War es hilfreich?

Lösung

trace ist die einfachste Methode, um für die Fehlersuche. Es ist nicht in IO genau aus dem Grunde, Sie darauf: keine Notwendigkeit, Code in der IO Monade zu heben. Es ist wie diese umgesetzt

trace :: String -> a -> a
trace string expr = unsafePerformIO $ do
    putTraceMsg string
    return expr

So gibt es IO hinter den Kulissen aber unsafePerformIO verwendet wird, davon zu entrinnen. Das ist eine Funktion, die möglicherweise Referenz Transparenz bricht, die Sie an den Typ IO a -> a suchen erraten kann und auch seinen Namen.

Andere Tipps

trace ist einfach unreine gemacht. Der Punkt des IO monadisch ist Reinheit zu erhalten (keine IO durch den Typen System unbemerkt), und die Reihenfolge der Ausführung von Anweisungen zu definieren, die sonst praktisch durch lazy evaluation undefiniert werden würden.

Auf eigene Gefahr können Sie aber dennoch hacken zusammen einige IO a -> a, das heißt durchführen unreine IO. Dies ist ein Hack und natürlich „leidet“ von lazy evaluation, aber das ist, was Spur tut einfach aus Gründen der Fehlersuche.

Dennoch sollte man aber wahrscheinlich andere Wege gehen für das Debuggen:

  1. Die Reduzierung der Notwendigkeit für das Debuggen von Zwischenwerten

    • Schreiben kleiner, wiederverwendbar, klar, generische Funktionen, deren Richtigkeit ist offensichtlich.
    • Kombinieren Sie die richtigen Stücke zu mehr richtig Stücke.
    • Tests oder interaktiv Stücke ausprobieren.
  2. Verwenden Haltepunkte usw. (Compiler-basierten Debugging)

  3. Die Nutzung generische Monaden. Wenn Ihr Code monadischen dennoch ist, schreiben sie unabhängig von einem konkreten Monade. Verwenden type M a = ... anstelle von einfachen IO .... Sie können danach leicht Monaden durch Transformatoren verbinden und eine Debug-Monade oben drauf setzen. Auch wenn der Bedarf an Monaden weg ist, könnten Sie einfach einfügen Identity a für reine Werte.

Für das, was es wert ist, gibt es eigentlich zwei Arten von „Debugging“ in Frage hier:

  • Protokollierung Zwischenwerte, wie beispielsweise den Wert ein bestimmte Unterausdruck auf jeden Anruf in eine rekursive Funktion hat
  • Überprüfen des Laufzeitverhalten der Auswertung eines Ausdrucks

In einer strengen imperativ Sprache dieser in der Regel zusammen. In Haskell, haben sie oft nicht:

  • Aufnahme Zwischenwert kann das Laufzeitverhalten ändern, beispielsweise durch die Auswertung von Bedingungen zu zwingen, die sonst weggeworfen würden.
  • Der eigentliche Prozess der Berechnung kann dramatisch unterscheiden sich von der scheinbaren Struktur eines Ausdrucks wegen Faulheit und gemeinsame Teilausdrücke.

Wenn Sie nur ein Protokoll der Zwischenwerte halten wollen, gibt es viele Möglichkeiten, dies zu tun - zum Beispiel, anstatt alles in IO heben, ein einfacher Writer Monade genügt, wobei dies äquivalente Funktionen zu machen zurückgeben 2 -Tupel ihres Ist-Ergebnis und ein Akkumulator Wert (eine Art von Liste, in der Regel).

Es ist auch nicht in der Regel notwendig zu setzen alles in die Monade, nur die Funktionen, die Notwendigkeit zu schreiben, um den „log“ Wert - zum Beispiel, können Sie, dass müssen möglicherweise nur die Unterausdrücke ausklammern in üblichen Weise mit fmaps und so weiter zu tun, Protokollierung, rein die Hauptlogik verlassen, wieder zusammenbauen dann die Gesamtberechnung durch reine Funktionen und Protokollierung Berechnungen zu kombinieren. Beachten Sie, dass Writer für ein Monade Art eines armseligen ist: ohne die Möglichkeit, zu lesen von das Protokoll, nur um es zu schreiben, jede Berechnung logisch unabhängig von seinem Kontext ist, die es leichter macht, jonglieren Dinge um.

Aber in einigen Fällen sogar, dass die viel des Guten -. Für viele reine Funktionen, nur Teilausdrücke zum Toplevel zu bewegen und versuchen, die Dinge aus in der REPL funktioniert ziemlich gut

Wenn Sie wirklich wollen Laufzeitverhalten von reinem Code überprüfen, jedoch - zum Beispiel, um herauszufinden, warum ein subexpression divergiert - es ist im allgemeinen keine Möglichkeit, so von anderem reinem Code zu tun - in der Tat, dies ist im wesentlichen die Definition der Reinheit. Also in diesem Fall, Sie haben keine andere Wahl, als zu bedienende Werkzeuge, die „außen“ die reine Sprache existieren: entweder unreine Funktionen wie unsafePerformPrintfDebugging - errr, ich meine trace - oder eine modifizierte Laufzeitumgebung, wie der GHCi Debugger <. / p>

trace neigt auch dazu, über zu bewerten sein Argument für den Druck, eine Menge von den Vorteilen der Trägheit in dem Prozess zu verlieren.

Wenn Sie warten, bis das Programm, bevor das Studium der Ausgabe beendet ist, dann Stapeln einer Writer Monade ist der klassische Ansatz einen Logger zu implementieren. Ich benutze diese hier ein Ergebnis zurück set aus unreinem HDBC Code.

Nun, da ganzes Haskell um Prinzip der lazy evaluation gebaut wird (so dass die Reihenfolge der Berechnungen ist in der Tat nicht-deterministisch), die Verwendung von printf die macht sehr wenig Sinn.

Wenn REPL + resultierende Werte überprüfen ist wirklich nicht genug für Ihren Debugging, Verpackung alles in IO ist die einzige Wahl (aber es ist nicht der richtige Weg von Haskell-Programmierung).

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