哈斯克尔的“懒惰的IO”?

时间:2010-02-18 16:21:31

标签: haskell io lazy-evaluation

我正在尝试在haskell进行一些小实验,想知道是否有可能利用懒惰来处理IO。我想写一个函数,它接受一个字符串(一个字符列表)并生成一个字符串,懒洋洋地。我希望能够懒洋洋地从IO中提取字符,因此每个字符一旦可用就会被处理,并且输出将在必要的字符可用时生成。但是,我不太确定是否/如何从IO monad中的输入生成一个懒惰的字符列表。

3 个答案:

答案 0 :(得分:14)

Haskell中的常规字符串IO是懒惰的。所以你的例子应该开箱即用。

这是一个例子,使用'interact'函数,它将一个函数应用于一个懒惰的字符流:

interact :: (String -> String) -> IO ()

让我们从输入流中过滤掉字母'e',懒洋洋地(即在恒定空间中运行):

main = interact $ filter (/= 'e')

如果您愿意,也可以使用getContents和putStr。他们都很懒。

运行它来过滤字典中的字母'e':

$ ghc -O2 --make A.hs
$ ./A +RTS -s < /usr/share/dict/words
...
               2 MB total memory in use (0 MB lost due to fragmentation)
...

所以我们看到它在2M的持续时间内运行。

答案 1 :(得分:7)

执行惰性IO的最简单方法涉及interactreadFilehGetContents等功能,如 dons 所示;在Real World Haskell一书中对这些内容进行了更广泛的讨论,您可能会发现它们很有用。如果记忆为我服务,所有这些功能最终都会使用 ephemient 提到的unsafeInterleaveIO来实现,所以你也可以按照你想要的方式构建自己的函数。

另一方面,明智的做法是unsafeInterleaveIO正如它所说的那样:不安全IO 。使用它 - 或基于它的功能 - breaks purity and referential transparency。这允许显然纯函数(即,不返回IO动作)在评估时影响外部世界,从相同参数产生不同结果,以及所有其他不愉快的事物。在实践中,使用unsafeInterleaveIO的最明智的方法不会导致问题,而简单的错误通常会导致明显且容易诊断的错误,但是你已经失去了一些很好的保证。

当然还有其他选择;您可以在Hackage上找到提供受限制的safer lazy IOconceptually different approaches的各种库。但是,鉴于在实际使用中很少出现问题,我认为大多数人都倾向于坚持使用内置的,技术上不安全的功能。

答案 2 :(得分:3)

unsafeInterleaveIO :: IO a -> IO a
     

unsafeInterleaveIO allos IO计算延迟延迟。传递IO a类型的值时,只有在需要IO的值时才会执行a。这用于实现惰性文件读取,请参阅System.IO.hGetContents

例如,main = getContents >>= return . map Data.Char.toUpper >>= putStr是懒惰的;当您将字符输入stdin时,您将在stdout上获得字符。

(这与写main = interact $ map Data.Char.toUpper相同,就像dons的回答一样。)