Ich bin neu im Bereich der funktionalen Programmierung und lerne gerade bei Learn You a Haskell , aber als ich dieses Kapitel durchgegangen bin, blieb ich beim unten stehenden Programm hängen:
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int
logNumber x = Writer (x, ["Got number: " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
b <- logNumber 5
return (a*b)
Ich habe diese Zeilen in einer .hs-Datei gespeichert und konnte sie nicht in mein ghci importieren, was sich beschwerte:
more1.hs:4:15:
Not in scope: data constructor `Writer'
Perhaps you meant `WriterT' (imported from Control.Monad.Writer)
Failed, modules loaded: none.
Ich habe den Typ mit dem Befehl ": info" geprüft:
Prelude Control.Monad.Writer> :info Writer
type Writer w = WriterT w Data.Functor.Identity.Identity
-- Defined in `Control.Monad.Trans.Writer.Lazy'
Aus meiner Sicht sollte dies so etwas wie "newtype Writer w a ...". Sein. Ich bin also verwirrt, wie man den Datenkonstruktor füttert und einen Writer erhält.
Ich denke, es könnte ein Versionsproblem sein und meine Ghci-Version ist 7.4.1
Das Paket Control.Monad.Writer
exportiert den Datenkonstruktor Writer
nicht. Ich denke, das war anders, als LYAH geschrieben wurde.
Stattdessen erstellen Sie Writer mit der Funktion writer
. Zum Beispiel in einer Ghci-Sitzung kann ich tun
ghci> import Control.Monad.Writer
ghci> let logNumber x = writer (x, ["Got number: " ++ show x])
Nun ist logNumber
eine Funktion, die Autoren erzeugt. Ich kann nach dem 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 -Schreiber zurückgibt, sondern alles, was die Typklasse MonadWriter
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 von multWithLog
als Writer [String] Int
angegeben. Jetzt kann ich es ausführen:
ghci> runWriter multWithLog
(15, ["Got number: 3","Got number: 5"])
Und Sie sehen, dass wir alle Zwischenvorgänge protokollieren.
Warum überhaupt die Typklasse MonadWriter
erstellen? Der Grund ist, mit Monadentransformatoren zu tun. Wie Sie richtig verstanden haben, ist der einfachste Weg, Writer
zu implementieren, als neuer Wrapper auf einem Paar:
newtype Writer w a = Writer { runWriter :: (a,w) }
Sie können dafü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 noch etwas anderes tut - beispielsweise, dass sie auch aus einer Umgebung lesen kann. Sie würden dies implementieren als
type RW r w a = ReaderT r (Writer w a)
Da sich der Writer im ReaderT
Monadentransformator befindet, können Sie tell w
nicht verwenden, wenn Sie die Ausgabe protokollieren möchten (da dies nur mit nicht umbrochenen Writern funktioniert), sondern müssen lift $ tell w
verwenden, der den tell
Funktion über ReaderT
, damit es auf die Monade des inneren Schriftstellers zugreifen kann. Wenn Sie Transformatoren mit zwei Ebenen benötigen (sagen wir, Sie möchten auch eine Fehlerbehandlung hinzufügen), müssen Sie lift $ lift $ tell w
verwenden. Dies wird schnell unhandlich.
Stattdessen können wir durch Definieren einer Typklasse jeden Monadentransformator-Wrapper um einen Writer in eine Instanz des Writers selbst umwandeln. Zum Beispiel,
instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)
das heißt, wenn w
ein Monoid ist und m
ein MonadWriter w
ist, dann ist ReaderT r m
auch ein MonadWriter w
. Dies bedeutet, dass wir die Funktion tell
direkt auf die transformierte Monade anwenden können, ohne sie explizit durch den Monadentransformator heben zu müssen.
Eine Funktion namens "writer" wird anstelle eines "Writer" -Konstruktors zur Verfügung gestellt. Veränderung:
logNumber x = Writer (x, ["Got number: " ++ show x])
zu:
logNumber x = writer (x, ["Got number: " ++ show x])
Ich habe eine ähnliche Nachricht erhalten, als ich den LYAH "Für ein paar Monaden mehr" mit dem Online-Haskell-Editor in repl.it probierte
Ich habe den Import geändert von:
import Control.Monad.Writer
zu:
import qualified Control.Monad.Trans.Writer.Lazy as W
So funktioniert mein Code jetzt so (mit Inspiration aus Kwangs Haskell Blog ):
import Data.Monoid
import qualified Control.Monad.Trans.Writer.Lazy as W
output :: String -> W.Writer [String] ()
output x = W.tell [x]
gcd' :: Int -> Int -> W.Writer [String] Int
gcd' a b
| b == 0 = do
output ("Finished with " ++ show a)
return a
| otherwise = do
output (show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b))
gcd' b (a `mod` b)
main :: IO()
main = mapM_ putStrLn $ snd $ W.runWriter (gcd' 8 3)