考虑一下这篇出色的博客文章中的以下缩写代码:
import System.Random (Random, randomRIO)
newtype Stream m a = Stream { runStream :: m (Maybe (NonEmptyStream m a)) }
type NonEmptyStream m a = (a, Stream m a)
empty :: (Monad m) => Stream m a
empty = Stream $ return Nothing
cons :: (Monad m) => a -> Stream m a -> Stream m a
cons a s = Stream $ return (Just (a, s))
fromList :: (Monad m) => [a] -> NonEmptyStream m a
fromList (x:xs) = (x, foldr cons empty xs)
到目前为止还不错 - 一种单子递归数据结构和一种从列表构建的方法。
现在考虑这个函数,它使用常量内存从流中选择(一致)随机元素:
select :: NonEmptyStream IO a -> IO a
select (a, s) = select' (return a) 1 s where
select' :: IO a -> Int -> Stream IO a -> IO a
select' a n s = do
next <- runStream s
case next of
Nothing -> a
Just (a', s') -> select' someA (n + 1) s' where
someA = do i <- randomRIO (0, n)
case i of 0 -> return a'
_ -> a
我没有掌握最后四行中发生的神秘的无限循环井;结果a'
取决于 上的递归someA
,它本身可能取决于a'
,但不一定。
我觉得递归工作者以某种方式在累加器中“累积”潜在值IO a
,但我显然无法很好地推理它。
谁能解释一下这个函数是如何产生它的行为的?