1

我很难想出一种方法来解释为什么以下两个看似等效的无限随机数序列 (infinf') 的定义被完全不同地评估:

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评估不同?getRandomrandomIO

4

1 回答 1

3

排序列表在 IO monad 中是严格的,但在 State monad 中可能是惰性的。Rand只是一个WrappedStateT,所以它可以是懒惰的:

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

evalRandIO在开始时只查询 IO 随机数生成器一次,然后在State获取的 上运行 -ful 计算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包含无限数量的副作用 IO 操作,因为每个操作都会randomIO修改全局随机数生成器。在执行所有效果之前,我们无法检查返回值。它类似于doing sequence $ repeat getLine,它只是重复读取行,并且永远不会返回。

于 2015-05-29T11:16:27.903 回答