Das Paket Control.Monad.Writer
exportiert den Datenkonstruktor nicht Writer
. Ich denke, das war anders, als LYAH geschrieben wurde.
Verwenden der MonadWriter-Typklasse in ghci
Stattdessen erstellen Sie Autoren mit der writer
Funktion. Zum Beispiel kann ich in einer Ghci-Sitzung tun
ghci> import Control.Monad.Writer
ghci> let logNumber x = writer (x, ["Got number: " ++ show x])
Jetzt logNumber
ist eine Funktion, die Schriftsteller erstellt. Ich kann nach seinem Typ fragen:
ghci> :t logNumber
logNumber :: (Show a, MonadWriter [String] m) => a -> m a
Was mir sagt, dass der abgeleitete Typ keine Funktion ist, die einen bestimmten Writer zurückgibt , sondern alles, was die MonadWriter
Typklasse implementiert . Ich kann es jetzt benutzen:
ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) }
:: Writer [String] Int
(Eingabe tatsächlich alle in einer Zeile eingegeben). Hier habe ich den Typ angegeben, der sein multWithLog
soll Writer [String] Int
. Jetzt kann ich es ausführen:
ghci> runWriter multWithLog
(15, ["Got number: 3","Got number: 5"])
Und Sie sehen, dass wir alle Zwischenoperationen protokollieren.
Warum ist der Code so geschrieben?
Warum überhaupt die MonadWriter
Typklasse erstellen ? Der Grund liegt in Monadentransformatoren. Wie Sie richtig erkannt haben, ist die einfachste Art der Implementierung Writer
ein Newtype-Wrapper über einem Paar:
newtype Writer w a = Writer { runWriter :: (a,w) }
Sie können hierfür eine Monadeninstanz deklarieren und dann die Funktion schreiben
tell :: Monoid w => w -> Writer w ()
das protokolliert einfach seine Eingabe. Angenommen, Sie möchten eine Monade, die über Protokollierungsfunktionen verfügt, aber auch etwas anderes tut - sagen wir, sie kann auch aus einer Umgebung lesen. Sie würden dies als implementieren
type RW r w a = ReaderT r (Writer w a)
Jetzt, da sich der Writer im ReaderT
Monadentransformator befindet, können Sie die Ausgabe nicht protokollieren, da Sie sie nur verwenden können tell w
(da dies nur bei nicht verpackten Writern funktioniert), sondern müssen sie verwenden lift $ tell w
, wodurch die tell
Funktion durch die "angehoben" wird, ReaderT
damit sie auf die zugreifen kann innere Schriftsteller Monade. Wenn Sie Transformatoren mit zwei Ebenen möchten (sagen wir, Sie möchten auch die Fehlerbehandlung hinzufügen), müssen Sie diese verwendenlift $ lift $ tell w
. Dies wird schnell unhandlich.
Stattdessen können wir durch Definieren einer Typklasse jeden Monadentransformator-Wrapper um einen Writer in eine Instanz des Writers selbst verwandeln. Beispielsweise,
instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)
das heißt, wenn w
es ein Monoid ist und ein m
ist MonadWriter w
, dann ReaderT r m
ist es auch ein MonadWriter w
. Dies bedeutet, dass wir die tell
Funktion direkt auf der transformierten Monade verwenden können, ohne uns die Mühe machen zu müssen, sie explizit durch den Monadentransformator anzuheben.