我很难找到一种方法来推理为什么以下两个看似无穷无等的随机数序列(inf
和inf'
的等价定义)被完全不同地评估:< / 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 . repeat
在getRandom
上的评估方式与randomIO
上的<Validation>
<Presentation>
<Slide Name = "Slide3">
<Shape Name = "Title">
<FontSize Value = "36"/>
</Shape>
</Slide>
<Shape Name = "Title">
<FontSize Value = "36"/>
</Shape>
</Presentation>
</Validation>
评估方式不同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
,它只是重复读取行,并且永远不会返回。