Search code examples
haskellrandommonadsstate-monad

Why does this list contain more than one distinct value?


I have code that looks like:

import qualified Data.Vector as V
import System.Random
import Control.Monad.State.Lazy
nextWeightedRandom :: State (StdGen, V.Vector Int) Int
nextWeightedRandom = do
  (g, fs) <- get
  let (i, g') = randomR (0, V.length fs - 1) g
  put (g', fs)
  return (fs V.! i)

weightedRandomList :: (StdGen, V.Vector Int) -> [Int]
weightedRandomList = evalState $ mapM (\_ -> nextWeightedRandom) [1..]

QuickCheck confirms that the values coming out of weightedRandomList are different and have roughly the distribution that I was hoping for (the Vector Int looks like [5, 5, 5, 10], so that nextWeightedRandom spits out 5 with probability 3/4 and 10 with probability 1/4).

I was able to get it working by guessing my way though the types, but I'm very confused --- isn't evalState getting run over a bunch of different copies of the same generator? Why doesn't the list produced look like [5, 5, 5, 5,...]?


Solution

  • Since you call put in nextWeightedRandom, the state gets updated on each iteration. So each time you call randomR ... g, it is using the generator output by the previous iteration.

    In fact, the question itself, "isn't evalState getting run over a bunch different copies of the same generator" is quite an interesting one, since evalState isn't handed many copies of anything! It is given a single generator, and does all its work starting from there.