使用严格的 monad 操作无限列表

Dou*_*ira 3 monads haskell lazy-evaluation strictness conduit

我有一个f :: [a] -> b对无限列表进行操作的函数(例如take 5takeWhile (< 100) . scanl (+) 0等等)。我想为这个函数提供由严格的单子动作(例如randomIO)生成的值。

这个问题中,我了解到repeatandsequence技巧方法不适用于严格的单子,如下例所示:

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
Run Code Online (Sandbox Code Playgroud)

因此,我考虑在单子上下文“内部”使用该函数。我受到这个循环编程示例的启发并尝试:

let loop = do
       x <- return 1
       (_, xs) <- loop
       return (take 5 xs, x:xs)
in  fst loop :: Identity [Int]
-- Overflows the stack
Run Code Online (Sandbox Code Playgroud)

import Control.Monad.Fix

fst <$> mfix (\(_, xs) -> do
   x <- return 1
   return (take 5 xs, x:xs)) :: Identity [Int]
-- Overflows the stack
Run Code Online (Sandbox Code Playgroud)

乃至

{-# 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
Run Code Online (Sandbox Code Playgroud)

但这些都不起作用。我还尝试了一种Conduit方法,即使在这种情况下也不起作用Identity

import Conduit

runConduitPure $ yieldMany [1..] .| sinkList >>= return . take 5
Run Code Online (Sandbox Code Playgroud)

因此我想知道:

  1. 为什么上述“循环”方法都不起作用?

  2. 如果存在一个不涉及unsafeInterleaveIO. (也许iteratee是,Arrow是?)

Ale*_*lec 5

我在这里使用 randomIO 只是为了简化示例。在实践中,我想处理通过套接字接收的消息

如果没有 ,这是不可能的unsafeInterleaveIO。归根结底的问题是IO行动必须按顺序进行。虽然引用透明值的评估顺序并不重要,但IO操作的评估顺序可能很重要。如果你想要一个通过套接字接收到的所有消息的惰性无限列表,你必须事先通知 Haskell 在操作序列中IO适合的位置(除非你使用unsafeInterleaveIO)。

您正在寻求的抽象Arrow称为,但它也存在严格单子的正确紧缩法则ArrowLoop的问题。

乍一看,它可能看起来MonadFix(通过mdoor体现mfix) 也是一个解决方案,但深入挖掘会发现fixIO存在问题,就像loop来自一样ArrowLoop

然而,有时动作必须一个接一个地运行的限制IO有点过分,但这就是unsafeInterleaveIO目的。引用文档

unsafeInterleaveIO允许IO延迟计算。当传递 类型的值时IO aIO仅当需要 的值时才会执行a


现在,即使您明确表示您想要unsafeInterleaveIO解决方案,因为我希望能够说服您这是可行的方法,这里是:

interweavingRepeatM :: IO a -> IO [a]
interweavingRepeatM action = unsafeInterleaveIO ((:) <$> action <*> interweavingRepeatM action)
Run Code Online (Sandbox Code Playgroud)

这是随机数的工作:

ghci> import System.Random
ghci> sourceOfRandomness <- interweavingRepeatM randomIO :: IO [Integer]
ghci> take 10 sourceOfRandomness
[-2002742716261662204,7803971943047671004,-8395318556488893887,-7372674153585794391,5906750628663631621,6428130029392850107,6453903217221537923,-8966011929671667536,6419977320189968675,-1842456468700051776]
Run Code Online (Sandbox Code Playgroud)