1

我很難找出一種方式來解釋爲什麼以下兩個看似等效的無限隨機數序列(infinf')的定義完全不同:無限隨機序列循環與randomIO但不與getRandom

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評估?

+0

你的意思是'main'終止和'main''掛起? –

+0

@AndrásKovács是的,正好。 –

+3

這兩者之間有一個重要的區別。 ''evalRandIO'使用'IO'來獲得初始化的生成器,從中生成一個無限列表,不需要更多的IO。 'randomIO'需要對每個隨機數字進行'IO'調用,所以當它試圖返回無限列表時,它會掛起(除非使用'unsaveInterleaveIO')。 – cchalmers

回答

3

排序列表在IO monad中是嚴格的,但在狀態monad中可能是惰性的。 Rand只是一個wrapped StateT,所以它可以偷懶:

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

evalRandIO之初查詢IO隨機數發生器只有一次,然後運行所取得的StdGenState -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類似,它只是重複讀取行,並且從不返回。