Бесконечные циклы случайных последовательностей с randomIO, но не с getRandom

Я с трудом пытаюсь найти способ рассуждать о том, почему следующие два, казалось бы, эквивалентных определения бесконечной последовательности случайных чисел (inf а также inf') оцениваются совершенно по-разному:

import Control.Monad.Random (Rand, evalRandIO, getRandom)
import System.Random        (Random, RandomGen, randomIO)

inf :: (RandomGen g, Random a) => Rand g [a]
inf = sequence (repeat getRandom)

inf' :: (Random a) => IO [a]
inf' = sequence (repeat randomIO)

-- OK
main = do
  i <- evalRandIO inf
  putStrLn $ show $ take 5 (i :: [Int])

-- HANGS
main' = do
  i <- inf'
  putStrLn $ show $ take 5 (i :: [Int])

когда вызывается, main' завершает и печатает 5 случайных чисел, тогда как main петли бесконечно - что вызывает sequence . repeat оценивать по-разному на getRandom чем это происходит на randomIO?

1 ответ

Списки секвенирования строгие в монаде IO, но, возможно, ленивые в монаде State. Rand это просто завернутыйStateTтак что может быть ленивым

type Rand g = RandT g Identity
newtype RandT g m a = RandT (StateT g m a)

evalRandIO сначала запрашивает генератор случайных чисел IO, а затем запускает State-полный расчет на приобретенном StdGen:

evalRandT :: (Monad m) => RandT g m a -> g -> m a
evalRandT (RandT x) g = evalStateT x g

evalRand :: Rand g a -> g -> a
evalRand x g = runIdentity (evalRandT x g)

evalRandIO :: Rand StdGen a -> IO a
evalRandIO x = fmap (evalRand x) newStdGen

По сравнению, sequence $ repeat randomIO содержит бесконечное количество побочных действий ввода-вывода, потому что каждый randomIO изменяет генератор случайных чисел. Мы не можем проверить возвращаемое значение, пока все эффекты не будут выполнены. Это похоже на выполнение sequence $ repeat getLine, который просто читает строки несколько раз и никогда не возвращается.

Другие вопросы по тегам