Wie schreibe ich eine Monade, die beim Ausführen jeder Anweisung in der Monade "Schritt i von N" ausgibt?

8

Ich bin mir nicht einmal sicher, ob das in irgendeiner Art von Monade möglich ist; Verstößt es gegen Monadengesetze? Aber es scheint etwas zu sein, das in irgendeiner Art von Konstrukt möglich sein sollte. Insbesondere gibt es eine Möglichkeit, etwas zu haben, dass ich etwas schreiben kann wie

%Vor%

und es würde

drucken %Vor%

Würde dies Template Haskell erfordern oder würde ein Monad funktionieren? (Und wenn Template Haskell benötigt wird, wie geht das?)

    
lobsterism 29.11.2013, 20:05
quelle

5 Antworten

10

Ich gehe davon aus, dass die Schritte automatisch angezeigt werden sollen, ohne dass Sie Code mit Logging-Anweisungen bestreuen müssen.

Das Problem mit Monaden ist, dass sie zu flexibel sind: Die "Form" des Rests der Berechnung kann jederzeit von Werten abhängen, die während der Berechnung selbst erhalten werden. Dies wird explizit in der Art von (>>=) , das ist m a -> (a -> m b) -> m b .

gemacht

Folglich gibt es keine feste Anzahl N der gesamten Schritte, die Sie kennen können, bevor Sie die Berechnung ausführen.

Haskell bietet jedoch zwei andere Abstraktionen an, die einen Teil der Macht und Flexibilität von Monaden in die Waagschale werfen, um vorher eine größere Anzahl von "statischen" Analysen durchzuführen: Anwendungsfunktoren und Pfeile .

Applicative Funktoren, obwohl sehr nützlich, sind vielleicht zu schwach für Ihre Bedürfnisse. Sie können keine Funktion in einem anwendungsspezifischen Funktor schreiben, der bei der Anwendung auf einen Wert diesen Wert auf die Konsole ausgibt. Dies wird in der Zeitung erklärt "Idiome sind blind, Pfeile sind akribisch, Monaden sind promiskuitiv ", die einige aufschlussreiche Beispiele der Grenzen jeder Abstraktion enthalten (anwendungsorientierte Funktoren werden in diesem Aufsatz" Idiome "genannt).

Pfeile bieten einen besseren Kompromiss zwischen Ausdruckskraft und Zugänglichkeit zur statischen Analyse. Die "Form" der Pfeilberechnungen ist in einer statischen Pipeline festgelegt. Daten, die während der Berechnung erhalten werden, können Auswirkungen später in der Pipeline beeinflussen (z. B. können Sie einen durch einen vorherigen Effekt in der Berechnung erhaltenen Wert drucken), aber nicht ändern die Form der Pipeline oder die Anzahl von Schritte.

Also, wenn Sie Ihre Berechnung mit Kleisli-Pfeilen ausdrücken könnten (die Pfeile einer Monade), könnten Sie vielleicht eine Art Pfeiltransformer ( nicht Monadetransformator) schreiben, der automatisierte Protokollierungsfunktionen hinzufügt.

Das Paket arrows bietet eine Reihe von Pfeiltransformatoren. Ich denke, StaticArrow könnte verwendet werden, um die Gesamtzahl der Schritte Aber Sie müssten noch einige Funktionen schreiben, um die Nachrichten tatsächlich zu senden.

Bearbeiten: Hier ist ein Beispiel, wie Sie die Anzahl der Schritte in einer Berechnung mithilfe von Pfeilen zählen können:

%Vor%

Beachten Sie, dass der Effekt von Schritt 3 von einem Wert beeinflusst wird, der in Schritt 2 erzeugt wurde. Dies kann nicht mithilfe von Applicatives durchgeführt werden.

Wir verwenden das (,) (Sum Int) applicative, das von StaticArrow benötigt wird, um die statischen Informationen zu kodieren (hier nur die Anzahl der Schritte).

Das Anzeigen der Schritte, wie sie ausgeführt werden, würde etwas mehr Arbeit erfordern.

Edit # 2 Wenn es sich um eine Befehlsfolge handelt, bei der kein Effekt von einem Wert eines vorherigen Effekts abhängt, können wir die Verwendung von Pfeilen vermeiden und die Schritte nur mit anwendungsspezifischen Funktoren zählen :

%Vor%

Data.Functor.Compose stammt aus dem transformers -Paket.

Edit # 3 Der folgende Code erweitert die vorherige Applicative Step-Counting-Lösung und verwendet das Paket pipes , um Benachrichtigungen zu senden. Die Pfeil-basierte Lösung könnte in ähnlicher Weise angepasst werden.

%Vor%     
danidiaz 29.11.2013, 21:32
quelle
5

Während ich denke, Daniel Díaz 'Pfeil Lösung ist der perfekte Weg, dies zu tun, gibt es sicherlich eine einfachere (die, ich sehe nur, er auch in den Kommentaren bereits angibt) zur Verfügung gestellt, wie in Ihrem Beispiel, keine Daten wird zwischen den verschiedenen Funktionsaufrufen übergeben.

Denken Sie daran, dass Funktionen, da Haskell faul ist, viele Dinge tun können, die Makros in anderen Sprachen benötigen. Insbesondere ist es kein Problem, eine Liste von IO Aktionen zu haben. (Absolut sicher auch: aus Gründen der Reinheit gibt es keine Möglichkeit, dass diese in Haskell "früh abgehen" könnten!) Dann kannst du einfach die Länge dieser Liste als Gesamtzählung nehmen, sie mit Druckanweisungen verschachteln und fertig machen. Alle in der Kernsprache, brauchen nicht TH!

%Vor%

Zu verwenden wie

%Vor%     
leftaroundabout 29.11.2013 23:28
quelle
3

Es gibt zwei Möglichkeiten, dass dies die Gesetze verletzen könnte, je nachdem, was Sie meinen.

Wenn zum Beispiel return als ein Schritt zählen würde, hätten Sie eine Verletzung, weil das erste Monad-Gesetz nicht gelten würde:

%Vor%

Wenn Sie zwei Schritte in eine andere benannte Funktion abstrahieren, gilt das ebenso als Entfernen eines Schrittes. Dann verletzen Sie auch die Monadengesetze, weil das dritte Monadengesetz nicht gilt:

%Vor%

Wenn Sie jedoch Befehle haben, die explizit die "step" -Ausgabe ausgeben, liegt kein Verstoß vor. Dies liegt daran, dass return dann überhaupt keine Ausgabe ausgeben kann, und das Sequenzieren von zwei Befehlen würde einfach ihre Schrittausgaben zusammenfügen. Hier ist ein Beispiel:

%Vor%

Beachten Sie, dass ich die Gesamtzahl der Schritte nicht einbeziehe. Es gibt keine Möglichkeit, auf diese Informationen für eine Monade zuzugreifen. Dafür empfehle ich Daniels Antwort, weil Applicative s eine ausgezeichnete Lösung für dieses Problem ist, die Anzahl der Schritte statisch ohne Template Haskell zu bestimmen.

    
Gabriel Gonzalez 30.11.2013 15:32
quelle
1

Es gibt viele Logger-Bibliotheken.

Wenn Sie an Monad-Logger interessiert sind - hier sind Sie: Control.Monad.Logger

Und im Hackage findest du weitere Bibliotheken

    
wit 29.11.2013 20:47
quelle
-1

Verwenden Sie einen Monad Transformer , um einen WriterT zu stapeln, der die Anzahl der >> und >>= wurden auf die zugrunde liegende Monade angewendet.

    
masonk 29.11.2013 21:06
quelle