Skip to content

Commit

Permalink
Write up an explanation and example of why FrozenGen is useful. Fix #146
Browse files Browse the repository at this point in the history
  • Loading branch information
lehins committed Jun 15, 2020
1 parent 8df71a6 commit 087de1f
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 4 deletions.
3 changes: 2 additions & 1 deletion random.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 43 additions & 3 deletions src/System/Random/Stateful.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -673,3 +712,4 @@ runSTGen_ g action = fst $ runSTGen g action
-- freezeGen = MWC.save
-- :}
--

0 comments on commit 087de1f

Please sign in to comment.