Der Datentyp AcidState a
ist kein unveränderlicher Wert bis zum Ende; Es enthält intern Verweise auf veränderbare Daten. Was in diesem Fall in Jessod-land gespeichert ist, ist einfach ein unveränderlicher Verweis auf diese Daten. Wenn Sie den Status aktualisieren, aktualisieren Sie den Wert im Basisdatentyp nicht, sondern stattdessen den Speicher, auf den er verweist.
Jeder Wert in der Haskell-Welt ist unveränderlich. Viele Dinge außerhalb des Reiches von Haskell sind jedoch nicht unveränderlich; Wenn Sie beispielsweise putStrLn
ausführen, ändert das Terminal seine Anzeige so, dass neuer Inhalt angezeigt wird. Die putStrLn
Aktion selbst ist ein unveränderlicher reiner Wert, aber sie beschreibt , wie man eine Aktion ausführt, die Mutation einbezieht.
Es gibt andere Funktionen, die ebenfalls Aktionen ergeben, die Mutationen ausführen; Wenn Sie ref <- newIORef 0
tun, erhalten Sie einen Wert, der eine Aktion beschreibt, die eine veränderbare Speicherzelle erstellt. Wenn Sie dann modifyIORef ref (+1)
tun, erhalten Sie einen Wert, der eine Aktion beschreibt, die den Wert in dieser Zelle um 1
erhöht. Der ref
Wert ist ein reiner Wert, es ist einfach eine Referenz auf eine veränderbare Zelle. Der Code ist auch rein funktional, weil jedes Stück nur eine Aktion beschreibt; nichts ist veränderbar innerhalb des Haskell-Programms.
So implementiert AcidState
seinen Status: durch Verwendung eines Systems, das den Status außerhalb der Haskell-Welt verwaltet.Dies ist nicht "so schlecht" wie in Sprachen wie C, da in Haskell die Wandlungsfähigkeit mit der Macht von Monaden gesteuert werden kann. Die Verwendung von AcidState
ist vollkommen sicher und beinhaltet nicht die Verwendung von unsafePerformIO
, soweit ich weiß.
Mit AcidState
in diesem Fall verwenden Sie openAcidState emptyStore
im IO
Monade einen neuen Säure Staat zu schaffen (die Zeile ist ein Wert, der eine IO Aktion beschreibt, die einen neuen Säure Zustand öffnet). Sie verwenden createCheckpointAndClose
, um den Säurestatus optional sicher auf der Festplatte zu speichern. Schließlich verwenden Sie die Funktion update'
, um den Inhalt eines Säurezustands zu ändern.
Um einen „kleinen Staat“ selbst zu erstellen IORef
s mit (Die einfachste Form von wandelbaren Zustand, mit Ausnahme vielleicht der ST
Monade), müssen Sie zunächst ein Feld wie diese zu Ihrem Gründungsdatentyp hinzufügen:
data VisitorCounter = VisitorCounter { visitorCounter :: IORef Int }
Sie tun dann:
main = do
counter <- newIORef 0
warpDebug 3000 (VisitorCounter counter)
In einem Handler können Sie den Zähler wie folgt ändern:
counter <- fmap visitorCounter getYesod
modifyIORef counter (+1)
count <- readIORef counter
-- ... display the count or something
Beachten Sie die Symmetrie zu AcidState
.
Für eine Website-Zähler, würde ich tatsächlich empfehlen TVar
s anstelle von IORef
s, wegen der Möglichkeit, dass mehrere Clients die Variable möglicherweise gleichzeitig ändern. Die Schnittstelle zu TVar
s ist jedoch sehr ähnlich.
Folgen Frage von Frage Autor up?
Ich habe { visitorCounter :: TVar Int }
in meiner Stiftung Art gelegt und der folgende Code in dem Handler:
counter <- fmap visitorCounter getYesod
count <- readTVar counter
Die erste Zeile stellt in Ordnung, aber der zweite wirft diesen Fehler:
Couldn't match expected type `GHandler sub0 Middleware t0'
with actual type `STM a0'
In the return type of a call of `readTVar'
In a stmt of a 'do' expression: count <- readTVar counter
Wie könnte ich das beheben?
Ich verstehe das nicht wirklich. Ich dachte, alle Daten in Haskell wären unveränderlich, sogar Monaden, da die "Do Notation" nur syntaktischer Zucker für funktionalen Code ist. Wenn diese Daten "heimlich" veränderbar sind, ist das keine Chance, dass Haskell, indem er annimmt, dass alles unveränderlich ist, keinen modifizierten Wert zurückgibt (weil angenommen wird, dass es unverändert ist). Sie scheinen zu vermuten, dass es wie "unsafePerformIO" funktioniert. Ist das wirklich so? – Clinton
Ich habe meine Antwort aktualisiert, bitte kommentieren, wenn Sie noch Fragen haben. – dflemstr
Danke für die aktualisierte Antwort. Folgefrage: Können Sie eine Implementierung einer trivialen Version von etwas wie "IORef" oder "TVar" anbieten, nur damit ich sehen kann, was sie tun (im Moment scheint es schwarze Magie zu sein)? Ich hätte auch gerne Informationen zur Thread-Sicherheit, aber ich denke, es wäre das Beste, wenn ich Ihre Antwort zuerst hinsichtlich der Implementierungsdetails durchlesen würde. – Clinton