diff --git a/random.cabal b/random.cabal index d7954b333..8d6e25e6f 100644 --- a/random.cabal +++ b/random.cabal @@ -142,7 +142,8 @@ test-suite doctests mwc-random >=0.13 && <0.15, primitive >=0.6 && <0.8, random -any, - unliftio >=0.2 && <0.3 + unliftio >=0.2 && <0.3, + vector >= 0.10 && <0.14 test-suite spec type: exitcode-stdio-1.0 diff --git a/src/System/Random/Stateful.hs b/src/System/Random/Stateful.hs index cd3346eab..4dea3f44a 100644 --- a/src/System/Random/Stateful.hs +++ b/src/System/Random/Stateful.hs @@ -623,9 +623,8 @@ runSTGen_ g action = fst $ runSTGen g action -- -- $implementmonadrandom -- --- Typically, a monadic pseudo-random number generator has facilities to save --- and restore its internal state in addition to generating pseudo-random --- pseudo-random numbers. +-- Typically, a monadic pseudo-random number generator has facilities to save and restore +-- its internal state in addition to generating pseudo-random numbers. -- -- Here is an example instance for the monadic pseudo-random number generator -- from the @mwc-random@ package: @@ -642,6 +641,46 @@ runSTGen_ g action = fst $ runSTGen g action -- > thawGen = MWC.restore -- > freezeGen = MWC.save -- +-- === @FrozenGen@ +-- +-- `FrozenGen` gives us ability to use any stateful pseudo-random number generator in its +-- immutable form, if one exists that is. This concept is commonly known as a seed, which +-- allows us to save and restore the actual mutable state of a pseudo-random number +-- generator. The biggest benefit that can be drawn from a polymorphic access to a +-- stateful pseudo-random number generator in a frozen form is the ability to serialize, +-- deserialize and possibly even use the stateful generator in a pure setting without +-- knowing the actual type of a generator ahead of time. For example we can write a +-- function that accepts a frozen state of some pseudo-random number generator and +-- produces a short list with random even integers. +-- +-- >>> import Data.Int (Int8) +-- >>> :{ +-- myCustomRandomList :: FrozenGen f m => f -> m [Int8] +-- myCustomRandomList f = +-- withMutableGen_ f $ \gen -> do +-- len <- uniformRM (5, 10) gen +-- replicateM len $ do +-- x <- uniformM gen +-- pure $ if even x then x else x + 1 +-- :} +-- +-- and later we can apply it to a frozen version of a stateful generator, such as `STGen`: +-- +-- >>> print $ runST $ myCustomRandomList (STGen (mkStdGen 217)) +-- [-50,-2,4,-8,-58,-40,24,-32,-110,24] +-- +-- or a @Seed@ from @mwc-random@: +-- +-- >>> import Data.Vector.Primitive as P +-- >>> print $ runST $ myCustomRandomList (MWC.toSeed (P.fromList [1,2,3])) +-- [24,40,10,40,-8,48,-78,70,-12] +-- +-- Alternatively, instead of discarding the final state of the generator, as it happens +-- above, we could have used `withMutableGen`, which together with the result would give +-- us back its frozen form. This would allow us to store the end state of our generator +-- somewhere for the later reuse. +-- +-- -- $references -- -- 1. Guy L. Steele, Jr., Doug Lea, and Christine H. Flood. 2014. Fast @@ -673,3 +712,4 @@ runSTGen_ g action = fst $ runSTGen g action -- freezeGen = MWC.save -- :} -- +