我正在尝试在haskell 中进行一个小实验,想知道是否可以利用惰性来处理IO。我想编写一个函数,它接受一个字符串(一个字符列表)并懒惰地产生一个字符串。然后我希望能够从 IO 懒惰地向它提供字符,这样每个字符都会在可用时立即处理,并且在必要的字符可用时生成输出。但是,我不太确定是否/如何从 IO monad 中的输入生成惰性字符列表。
3 回答
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 足迹运行。
做惰性 IO 的最简单方法涉及诸如interact
、readFile
、hGetContents
之类的函数,正如dons所说的那样;在Real World Haskell一书中对这些内容进行了更深入的讨论,您可能会发现它们很有用。如果我没记错的话,所有这些功能最终都是使用unsafeInterleaveIO
那个ehemient提到的来实现的,所以如果你愿意,你也可以这样构建你自己的函数。
另一方面,注意这unsafeInterleaveIO
正是它在锡上所说的内容可能是明智的:unsafe IO。使用它——或基于它的函数——破坏了纯度和引用透明度。这允许明显的纯函数(即不返回IO
动作)在评估时影响外部世界,从相同的参数产生不同的结果,以及所有其他不愉快的事情。在实践中,大多数明智的使用方式unsafeInterleaveIO
不会导致问题,简单的错误通常会导致明显且易于诊断的错误,但您已经失去了一些很好的保证。
当然,还有其他选择;你可以在 Hackage 上找到各种库,这些库提供了受限的、更安全的惰性 IO或概念上不同的方法。但是,鉴于在实际使用中很少出现问题,我认为大多数人倾向于坚持使用内置的、技术上不安全的功能。
unsafeInterleaveIO :: IO a -> IO a
unsafeInterleaveIO
allosIO
计算被延迟延迟。当传递一个 type 的值时IO a
,IO
只有在需要 的值时才会执行a
。这用于实现延迟文件读取,请参阅System.IO.hGetContents
.
例如,main = getContents >>= return . map Data.Char.toUpper >>= putStr
是懒惰的;当您将字符输入标准输入时,您将在标准输出上获得字符。
(这与写作相同main = interact $ map Data.Char.toUpper
,如唐斯的回答。)