English 中文(简体)
Haskell和IO流效果
原标题:
  • 时间:2009-04-16 00:15:02
  •  标签:

考虑以下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

<强>更新:< /强>

谢谢你们所有人的帮助和周到的反馈。我没有见过<代码> < /代码>操作符序列,这是有用的了解。我想到一个比较优雅的方式传送IO(字符串)值,而不是字符串,但作用有限的编程风格,因为我想其他流函数作用于字符串本身,而不是行为可以产生一个字符串。但是,基于思考其他的反应,我想我明白为什么这是无法解决的。在简单的情况下,我提出,我真正想要的是<代码> < /代码>操作符序列,因为我认为流排序隐含一个排序的操作。事实上,没有这样的顺序必然隐含。这变得更加清晰,我当我想到流函数需要两个流作为输入(例如成对除了两个流)。如果两个IO流执行“输入”,这些IO操作的顺序是未定义的(当然,除非我们自己定义通过测序IO单子)。问题解决了,谢谢大家!

最佳回答

这个代码:

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) > < /代码/值只是一个函数像任何其他可放在一个列表,通过周围,等。注意,做语法并不发生在“effectfulStreamFunc”——它实际上是一个纯函数,尽管“输入输出”的签名。只有当我们运行<代码> < /代码>序列的主要影响真的发生。

问题回答

我不真正了解你的主要目标,但使用putStrLn结果评估的整个列表,因为它执行时将评估论证。考虑

import IO

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

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

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

这导致”(“a”、“b”)”,而使用putStrLn版本导致异常。

t Tomh如前所述,您可以真正做到“安全”,因为你打破了引用Haskell的透明度。你试图执行副作用懒洋洋地,但事懒惰是你以什么样的顺序不保证是否得到评估,所以在Haskell,当你告诉它执行的副作用,它总是执行,在指定的顺序。(即在这种情况下,副作用的递归调用<代码> effectfulStreamFunc < /代码> <代码>之前返回> < /代码,因为这是它们的顺序列出)你不能做这个懒洋洋地不使用不安全的。

你可以尝试使用类似<代码> unsafeInterleaveIO > < /代码,这是懒惰的IO(例如<代码> hGetContents > < /代码)在Haskell中实现,但它也有自己的问题;你说,你别想用“不安全”的东西。

import System.IO.Unsafe (unsafeInterleaveIO)

effectfulStreamFunc :: [String] -> IO [String]
effectfulStreamFunc [] = return []
effectfulStreamFunc (x:xs) = unsafeInterleaveIO $ 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

在这种情况下,输出看起来是这样的

"a
["a"b
,"b"]"




相关问题
热门标签