Давайте начнем с самого начала:
type Rand a = State StdGen a
Эта строка говорит о том, что Rand a
тип синоним типа State
, состояние которого определяется StdGen
и чье окончательное значение типа a
. Это будет использоваться для хранения состояния генератора случайных чисел между каждым запросом для случайного числа.
Код для getRandom
может быть преобразован в делать обозначение:
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
runRand
функция принимает начальное семя n
и значение r
типа Rand a
(который, помните, это просто синоним State StdGen a
). Он создает новый генератор с mkStdGen n
и подает его на evalState r
. Функция evalState
просто оценивает возвращаемое значение типа State s a
, игнорируя состояние.
Опять же, мы можем преобразовать runRandIO
в do
нотации:
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
Наконец, getRandoms
принимает число n
, представляющее количество случайных значений, которые вы хотите создать. Он создает список [1..n]
и применяет getRandom
к списку. Обратите внимание, что фактические значения в [1..n]
не используются (вы можете сказать, потому что функция лямбда начинается с \_ -> ...
). В списке есть только что-то с правильным количеством элементов. Так как getRandom
возвращает монадическое значение, мы используем mapM
для сопоставления по списку, что приводит к правильному потоку состояния (то есть StdGen
) через каждый из вызовов до getRandom
.