Wie kann ich Zustand in einer versteckten Weise in Haskell (wie der PRNG der Fall ist) initialisiert werden?
-
13-09-2019 - |
Frage
Ich ging durch einige Tutorials über die staatliche Monade und ich glaube, ich auf die Idee kam.
Zum Beispiel, wie in diesem netten Tutorial :
import Data.Word
type LCGState = Word32
lcg :: LCGState -> (Integer, LCGState)
lcg s0 = (output, s1)
where s1 = 1103515245 * s0 + 12345
output = fromIntegral s1 * 2^16 `div` 2^32
getRandom :: State LCGState Integer
getRandom = get >>= \s0 -> let (x,s1) = lcg s0
in put s1 >> return x
OK, so kann ich getRandom verwenden:
*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 1
(16838,1103527590)
Aber ich muss noch den Samen der PRNG jedes Mal passieren ich es nennen. Ich weiß, dass die in Haskell-Implementierungen verfügbar PRNG, dass nicht braucht:
Prelude> :module Random
Prelude Random> randomRIO (1,6 :: Int)
(...) -- GHC prints some stuff here
6
Prelude Random> randomRIO (1,6 :: Int)
1
Also habe ich wahrscheinlich den Staat Monade falsch verstanden, weil das, was ich in den meisten Tutorials sehen konnte scheint nicht „persistent“ Staat, sondern nur eine bequeme Art und Weise zu seinen Zustand fädeln.
So ... Wie kann ich Zustand, der automatisch initialisiert (möglich von einigen Funktion, die Zeit und andere nicht-sehr vorhersagbaren Daten), wie das Zufall Modul verwendet das?
Vielen Dank!
Lösung
randomRIO
verwendet die IO
Monade. Dies scheint im Interpreter schön zu arbeiten, weil der Dolmetscher arbeitet auch in der IO
Monade. Das ist, was Sie in Ihrem Beispiel sehen; Sie können nicht wirklich tun auf der obersten Ebene im Code -. Sie es trotzdem in einem Do-Ausdruck bringen würden wie alle Monaden müssen
Im allgemeinen Code sollten Sie die IO Monade vermeiden, denn wenn Ihr Code die IO Monade verwendet, es für immer zu externen Zustand gebunden ist - man kann nicht aus ihm heraus (dh, wenn Sie Code haben, der IO Monade verwendet jeder Code, die es auch nennt den IO Monade verwenden; es keinen sicheren Weg gibt, um es zu „get out“). So ist die IO
Monade nur für Dinge verwendet werden, sollte die äußere Umgebung wie den Zugriff auf Dinge, wo es unbedingt erforderlich ist.
Für Dinge wie lokale Selbstversorger-Zustand, sollten Sie nicht die IO Monade verwenden. Sie können die State
Monade verwenden, wie Sie erwähnt haben, oder Sie können die ST
Monade verwenden. Die ST Monade enthält viele der gleichen Eigenschaften wie der IO-Monade; das heißt, es ist STRef
veränderbare Zellen, die analog zu IORef
. Und das Schöne an ST IO im Vergleich ist, dass, wenn Sie fertig sind, können Sie runST
auf einem ST-Monade nennen das Ergebnis der Berechnung aus der Monade zu erhalten, die Sie nicht mit IO tun können.
Wie bei „versteckt“ dem Staat, die gerade kommt als Teil der Syntax der do-Ausdrücke in Haskell für Monaden. Wenn Sie denken, Sie brauchen, um explizit den Zustand übergehen, dann sind Sie nicht die Monade Syntax korrekt verwendet wird.
Hier ist Code, der IOREF im IO-Monade verwendet:
import Data.IORef
foo :: IO Int -- this is stuck in the IO monad forever
foo = do x <- newIORef 1
modifyIORef x (+ 2)
readIORef x
-- foo is an IO computation that returns 3
Hier ist Code, der die ST Monade verwendet:
import Control.Monad.ST
import Data.STRef
bar :: Int
bar = runST (do x <- newSTRef 1
modifySTRef x (+ 2)
readSTRef x)
-- bar == 3
Die Einfachheit des Codes ist im Wesentlichen gleich sind; außer dass in diesem Fall wir den Wert aus der Monade bekommen, und in den ehemaligen können wir nicht ohne sie in einer anderen IO Berechnung setzen.
Andere Tipps
secretStateValue :: IORef SomeType
secretStateValue = unsafePerformIO $ newIORef initialState
{-# NOINLINE secretStateValue #-}
jetzt Ihren secretStateValue mit normalen readIORef und writeIORef .
Also habe ich wahrscheinlich den Staat Monade falsch verstanden, weil das, was ich in den meisten Tutorials sehen konnte, scheint nicht „persistent“ Zustand zu sein, sondern nur eine bequeme Art und Weise Zustand fädeln.
Der Zustand Monade ist genau über Zustand durch einigen Umfang Threading.
Wenn Sie Top-Level-Zustand wollen, ist das außerhalb der Sprache (und Sie werden eine globale veränderbare Variable verwenden). Beachten Sie, wie dies wahrscheinlich Thread-Sicherheit des Codes kompliziert - wie wird dieser Zustand initialisiert? und wann? Und von dem Thread?