この優れたブログ投稿からの次の省略コードを検討してください。
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
私は最後の4行で起こっている無限の神秘的な循環井戸を把握していません。結果a'
は、の再帰に依存します。再帰someA
自体はに依存する可能性がありますa'
が、必ずしもそうとは限りません。
再帰ワーカーがアキュムレータに潜在的な値を何らかの形で「蓄積」しているという雰囲気がありますが、IO a
それについて十分に推論することはできません。
この関数がどのように動作を生成するかについて誰かが説明できますか?