无限随机序列循环使用randomIO但不使用getRandom

时间:2015-05-29 10:52:02

标签: haskell random lazy-evaluation infinite evaluation-strategy

我很难找到一种方法来推理为什么以下两个看似无穷无等的随机数序列(infinf'的等价定义)被完全不同地评估:< / p>

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 . repeatgetRandom上的评估方式与randomIO上的<Validation> <Presentation> <Slide Name = "Slide3"> <Shape Name = "Title"> <FontSize Value = "36"/> </Shape> </Slide> <Shape Name = "Title"> <FontSize Value = "36"/> </Shape> </Presentation> </Validation> 评估方式不同1}}?

1 个答案:

答案 0 :(得分:3)

排序列表在IO monad中是严格的,但在State monad中可能是懒惰的。 Rand只是wrapped StateT,所以它可能很懒:

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

evalRandIO在开头只查询一次IO随机数生成器,然后对获取的State运行StdGen - ful计算:

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修改全局随机数生成器。我们无法检查返回值,直到执行所有效果。它类似于执行sequence $ repeat getLine,它只是重复读取行,并且永远不会返回。