Haskell流与IO效果

Geo*_*off 11 io haskell stream

考虑以下Haskell程序.我试图以"流式"编程,其中函数在流上运行(这里简单地作为列表实现).像normalStreamFunc这样的东西适用于懒惰列表.我可以将无限列表传递给normalStreamFunc并有效地获取另一个无限列表,但是将函数映射到每个值.像effectivefulStreamFunc这样的东西不能很好地工作.IO操作意味着我需要先评估整个列表,然后才能提取单个值.例如,程序的输出是这样的:

a
b
c
d
"[\"a\",\"b\"]"
Run Code Online (Sandbox Code Playgroud)

但我想要的是一种编写effectfulStreamFunc的方法,以便程序生成:

a
b
"[\"a\",\"b\"]"
Run Code Online (Sandbox Code Playgroud)

留下剩下的行动没有评估.我可以想象一个使用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
Run Code Online (Sandbox Code Playgroud)

更新:

谢谢大家的有益和深思熟虑的反馈.我之前没有见过sequence操作员,这对了解有帮助.我曾想过一种(不那么优雅)传递IO(字符串)值而不是字符串的方法,但对于编程风格的用途有限,因为我希望其他流函数对字符串本身起作用,而不是可以产生字符串的动作.但是,基于对其他回答的思考,我想我明白为什么这一般无法解决.在我提出的简单案例中,我真正想要的是sequence运算符,因为我认为流顺序意味着对操作的排序.事实上,不一定暗示这种排序.当我考虑以两个流作为输入的流函数时(例如,成对添加两个流),这对我来说变得更加清晰.如果两个"传入"流都执行IO,那么这些IO动作的顺序是不确定的(当然,除非我们通过在IO monad中对它进行排序来定义它).问题解决了,谢谢大家!

Jes*_*sak 7

这段代码怎么样:

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

而不是effectfulStreamFunc实际上正在执行任何IO,而是创建一个要执行的IO操作列表.(注意类型签名的更改.)然后main函数接受其中2个操作,运行它们并打印结果:

a
b
"[\"a\",\"b\"]"
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为类型IO (String)只是一个函数/值,你可以放入列表,传递等等.注意do语法不会出现在"effectfulStreamFunc" - 它实际上是一个纯函数,尽管"IO"在其签名中.只有当我们sequence在主要的那些上运行时才会发生效果.