Das Übergeben von Staaten ist oft mühsam, fehleranfällig und behindert das Refactoring. Versuchen Sie beispielsweise, einen Binärbaum oder einen Rosenbaum in der folgenden Reihenfolge zu kennzeichnen:
%Vor% Hier musste ich die Zustände manuell in der richtigen Reihenfolge beschriften, die richtigen Zustände weitergeben und sicherstellen, dass sowohl die Beschriftungen als auch die Kindknoten in der richtigen Reihenfolge im Ergebnis sind (beachten Sie, dass foldr
naiv verwendet wird) oder foldl
für die Kindknoten hätte leicht zu einem falschen Verhalten führen können.)
Wenn ich versuche, den Code in die Vorbestellung zu ändern, muss ich auch Änderungen vornehmen, die leicht falsch sind:
%Vor%Beispiele:
%Vor%Vergleichen Sie die State-Monad-Lösung:
%Vor%Dieser Code ist nicht nur prägnanter und einfacher zu schreiben, auch die Logik, die zur Kennzeichnung vor oder nach der Bestellung führt, ist viel transparenter.
PS: Bonusanwendungsstil:
%Vor% Als Beispiel für meine Kommentar oben, können Sie Code mit der State
Monade wie
Beachten Sie, dass Sie bei Verwendung zusätzlicher Operatoren von Control.Lens
auch incrCnt
und logMsg
als
Dies ist ein weiterer Vorteil der Verwendung von State
in Kombination mit der lens
-Bibliothek, aber zum Vergleich verwende ich sie nicht in diesem Beispiel. Um den obigen äquivalenten Code mit nur passendem Argument zu schreiben, würde es eher wie
An dieser Stelle ist es nicht so schlimm, aber sobald wir zum nächsten Schritt kommen, werden Sie sehen, wo die Code-Duplizierung wirklich ankommt:
%Vor% Ein weiterer Vorteil, dies in einer Monad
-Instanz zu erreichen, ist, dass Sie die volle Leistung von Control.Monad
und Control.Applicative
damit nutzen können:
Dies ermöglicht viel mehr Flexibilität bei der Verarbeitung von Werten, die zur Laufzeit im Vergleich zu statischen Werten berechnet werden.
Der Unterschied zwischen der manuellen Statusübergabe und der Verwendung der State
-Monade ist einfach, dass die State
-Monade eine Abstraktion über den manuellen Prozess ist. Es passt auch zu mehreren anderen allgemeineren Abstraktionen, wie Monad
, Applicative
, Functor
und einige andere. Wenn Sie auch den Transformer StateT
verwenden, können Sie diese Operationen mit anderen Monaden zusammenstellen, z. B. IO
. Kannst du das alles ohne State
und StateT
machen? Natürlich kannst du das, und es gibt niemanden, der dich davon abhält, aber der Punkt ist, dass State
dieses Muster abstrahiert und dir Zugriff auf eine riesige Toolbox mit allgemeineren Werkzeugen gibt. Außerdem bewirkt eine kleine Änderung der oben genannten Typen, dass die gleichen Funktionen in mehreren Kontexten funktionieren:
Diese funktionieren jetzt mit App
oder mit StateT MyState IO
oder einem anderen Monad Stack mit einer MonadState
Implementierung. Es macht es deutlich wiederverwendbarer als die einfache Argumentübergabe, was nur durch die Abstraktion StateT
möglich ist.
Nach meiner Erfahrung klickt der Punkt vieler Monads nicht wirklich, bis Sie zu größeren Beispielen kommen, also hier eine Beispielverwendung von State
(gut, StateT ... IO
), um eine eingehende Anfrage an einen Webservice zu analysieren .
Das Muster ist, dass dieser Webservice mit einer Reihe von Optionen verschiedener Typen aufgerufen werden kann, obwohl alle außer einer der Optionen über anständige Standardwerte verfügen. Wenn ich eine eingehende JSON-Anfrage mit einem unbekannten Schlüsselwert erhalte, sollte ich mit einer entsprechenden Nachricht abbrechen. Ich benutze den Zustand, um zu verfolgen, was die aktuelle Konfiguration ist, und was der Rest der JSON-Anfrage ist, zusammen mit einer Reihe von Accessor-Methoden.
(Basierend auf dem aktuell in Produktion befindlichen Code, wobei sich die Namen von allem geändert haben und die Details dessen, was dieser Dienst tatsächlich tut, verdeckt)
%Vor%Tags und Links haskell monads purely-functional state-monad