Бесконечные циклы случайных последовательностей с 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
, который просто читает строки несколько раз и никогда не возвращается.