Vamos a empezar por el principio:
type Rand a = State StdGen a
Esta línea le dice que Rand a
es un sinónimo de tipo para State
, cuyo estado viene dada por StdGen
y cuyo valor final es de tipo a
. Esto se usará para almacenar el estado del generador de números aleatorios entre cada solicitud de un número aleatorio.
El código para getRandom
se puede convertir en notación hacer:
getRandom :: (Random a) => Rand a
getRandom = do
r <- get -- get the current state of the generator
let (a,g) = random r in do -- call the function random :: StdGen -> (a, StdGen)
put g -- store the new state of the generator
return a -- return the random number that was generated
La función runRand
toma una semilla inicial n
y un valor r
de tipo Rand a
(que, recordemos, es sólo un sinónimo de State StdGen a
). Crea un generador nuevo con mkStdGen n
y lo alimenta a evalState r
. La función evalState
solo evalúa el valor de retorno de un tipo State s a
, ignorando el estado.
Una vez más, podemos convertir en runRandIO
do
notación:
runRandIO :: Rand a -> IO a
runRandIO r = do
rnd <- randomIO -- generate a new random number using randomIO
return (runRand rnd r) -- use that number as the initial seed for runRand
Por último, getRandoms
toma un número n
que representa el número de valores aleatorios que desea generar. Construye una lista [1..n]
y aplica getRandom
a la lista. Tenga en cuenta que los valores reales en [1..n]
no se utilizan (puede saber porque la función lambda comienza con \_ -> ...
). La lista está allí para tener algo con la cantidad correcta de elementos. Dado que getRandom
devuelve un valor monádico, usamos mapM
para mapear sobre la lista, lo que hace que el estado (es decir, StdGen
) se enhebre correctamente a través de cada una de las llamadas al getRandom
.