我有一个f :: [a] -> b
在无限列表上运行的函数(例如take 5
,takeWhile (< 100) . scanl (+) 0
等等)。我想为这个函数提供由严格的一元动作(例如randomIO
)生成的值。
从这个问题中,我了解到repeat
andsequence
技巧方法不适用于严格的单子,如下面的示例所示:
import Control.Monad.Identity
take 5 <$> sequence (repeat $ return 1) :: Identity [Int]
-- returns `Identity [1,1,1,1,1]`
-- works because Identity is non-strict
take 5 <$> sequence (repeat $ return 1) :: IO [Int]
-- returns `*** Exception: stack overflow`
-- does not work because IO is strict
所以,相反,我考虑在单子上下文“内部”使用函数。我受到这个循环编程示例的启发并尝试:
let loop = do
x <- return 1
(_, xs) <- loop
return (take 5 xs, x:xs)
in fst loop :: Identity [Int]
-- Overflows the stack
和
import Control.Monad.Fix
fst <$> mfix (\(_, xs) -> do
x <- return 1
return (take 5 xs, x:xs)) :: Identity [Int]
-- Overflows the stack
乃至
{-# LANGUAGE RecursiveDo #-}
import System.Random
loop' = mdo
(xs', xs) <- loop xs
return xs'
where loop xs = do
x <- randomIO
return (take 5 xs, x:xs)
print $ loop'
-- Returns a list of 5 identical values
但这些都不起作用。我还尝试了Conduit
一种即使在这种情况下也不起作用的方法Identity
:
import Conduit
runConduitPure $ yieldMany [1..] .| sinkList >>= return . take 5
因此我想知道:
为什么上面的“循环”方法都不起作用?
如果存在不涉及的解决方案
unsafeInterleaveIO
。(也许iteratee
s,Arrow
s?)