具有 IO 效果的 Haskell 流

标签 haskell io stream

考虑以下 Haskell 程序。我正在尝试以“流样式”进行编程,其中函数对流进行操作(此处仅作为列表实现)。像 normalStreamFunc 这样的东西非常适合惰性列表。我可以将一个无限列表传递给 normalStreamFunc 并有效地得到另一个无限列表,但函数映射到每个值。像 effectfulStreamFunc 这样的东西效果不佳。 IO 操作意味着我需要先评估整个列表,然后才能得出单个值。例如,程序的输出是这样的:

a
b
c
d
"[\"a\",\"b\"]"

但我想要的是一种编写 effectfulStreamFunc 的方法,以便程序产生这个:
a
b
"[\"a\",\"b\"]"

不评估剩余的操作。我可以想象一个使用 unsafePerformIO 的解决方案,但假设我将其从桌面上移除。这是程序:
import IO

normalStreamFunc :: [String] -> [String]
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs

effectfulStreamFunc :: [String] -> IO [String]
effectfulStreamFunc [] = return []
effectfulStreamFunc (x:xs) = do
    putStrLn x
    rest <- effectfulStreamFunc xs
    return (reverse(x):rest)

main :: IO ()
main = do
     let fns = ["a", "b", "c", "d"]
     es <- effectfulStreamFunc fns
     print $ show $ take 2 es

更新:

感谢大家的有益和周到的反馈。我没看过sequence运算符(operator)之前,这有助于了解。我曾想过一种(不太优雅的)方法来传递 IO(字符串)值而不是字符串,但是对于有用性有限的编程风格,因为我希望其他流函数对字符串本身起作用,而不是对可以产生字符串的 Action 。但是,基于对其他回应的思考,我想我明白为什么这通常是无法解决的。在我介绍的简单案例中,我真正想要的是 sequence运算符,因为我认为流排序暗示了对 Action 的排序。事实上,没有必要隐含这样的排序。当我想到一个将两个流作为输入的流函数(例如,成对添加两个流)时,这对我来说变得更清楚了。如果两个“传入”流都执行 IO,则这些 IO 操作的顺序是未定义的(当然,除非我们通过自己在 IO monad 中对其进行排序来定义它)。问题已解决,谢谢大家!

最佳答案

这段代码怎么样:

import IO

normalStreamFunc :: [String] -> [String]
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs

effectfulStreamFunc :: [String] -> [IO (String)]
effectfulStreamFunc [] = []
effectfulStreamFunc (x:xs) =
    let rest = effectfulStreamFunc xs in
        (putStrLn x >> return x) : rest

main :: IO ()
main = do
     let fns = ["a", "b", "c", "d"]
     let appliedFns = effectfulStreamFunc fns
     pieces <- sequence $ take 2 appliedFns
     print $ show $ pieces

而不是 effectfulStreamFunc 实际执行任何 IO,而是创建一个要执行的 IO 操作列表。 (注意类型签名更改。)然后主函数执行其中 2 个操作,运行它们并打印结果:
a
b
"[\"a\",\"b\"]"

这是因为类型 IO (String)只是一个函数/值,就像您可以放入列表、传递等的任何其他函数/值。请注意,do 语法不会出现在“effectfulStreamFunc”中 - 它实际上是一个纯函数,尽管它的“IO”签名。只有当我们运行 sequence对那些主要的影响是否真的发生了。

关于具有 IO 效果的 Haskell 流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/754279/

相关文章:

haskell - 基于字节序的条件编译

c++ - 从 C++ 调用 Haskell

c - 从文件读取到数组

c++ - ios::adjustfield 的目的是什么?

javascript - 如何在 Node 中通过 http 管道流检测客户端关闭连接?

haskell - 合并 Haskell 中相似的列表列表

haskell - FP 中模式匹配相对于条件的优势是什么(通俗地说)?

java - 克服服务器程序中的阻塞写入

java.io.IOException : No space left on device Android 异常

stream - 使用 ffmpeg 加速开始流式录制