haskell - 是什么导致了这个 "delayed read on closed handle"错误?

标签 haskell ghc lazy-io

我刚刚从最新来源安装了 GHC,现在我的程序给了我一条关于“关闭句柄延迟读取”的错误消息。这是什么意思?

最佳答案

基本的惰性 I/O 原语 hGetContents , 产生 String懒惰地——它只根据需要从句柄中读取,以生成程序实际需要的字符串部分。但是,一旦关闭句柄,就无法再从句柄中读取,如果您尝试检查尚未读取的字符串部分,您将收到此异常。例如,假设你写

main = do
  most <- withFile "myfile" ReadMode
                (\h -> do
                         s <- hGetContents h
                         let (first12,rest) = splitAt 12 s
                         print first12
                         return rest)
  putStrLn most
GHC 开通myfile并将其设置为懒惰地读取我们绑定(bind)到 s 的字符串.它实际上并没有开始从文件中读取。然后它设置了一个惰性计算以在 12 个字符后拆分字符串。然后print强制计算,GHC 读取 myfile block 至少 12 个字符长,并打印出前十二个字符。然后在 withFile 时关闭文件。完成,并尝试打印出其余部分。如果文件比 GHC 缓冲的 block 长,一旦到达 block 的末尾,您将得到延迟读取异常。
如何避免这个问题
在关闭文件或从 withFile 返回之前,您需要确保您已实际阅读了所需的所有内容。 .如果您传递给 withFile 的函数只需执行一些 IO 并返回一个常量(例如 () ),您无需担心这一点。如果您需要它从惰性读取中产生一个真正的值,您需要确保在返回之前充分强制该值。在上面的示例中,您可以使用 Control.DeepSeq 中的函数或运算符将字符串强制为“正常形式”。模块:
return $!! rest
这确保了字符串的其余部分在 withFile 之前被实际读取。关闭文件。 $!!如果您返回的是从文件内容计算的某个值,则该方法也可以很好地工作,只要它是 NFData 的实例即可。类(class)。在这种情况下,以及许多其他情况下,最好将用于处理文件内容的其余代码简单地移动到传递给 withFile 的函数中。 , 像这样:
main = withFile "myfile" ReadMode
            (\h -> do
                     s <- hGetContents h
                     let (first12,rest) = splitAt 12 s
                     print first12
                     putStrLn rest)
另一个要考虑的函数是 readFile . readFile保持文件打开,直到它完成读取文件。您应该只使用 readFile但是,如果您知道您实际上将需要文件的全部内容,否则您可能会泄漏文件描述符。
历史
根据 Haskell 报告,一旦句柄关闭,字符串的内容就固定了。
过去,GHC 只是在句柄关闭时缓冲的任何内容的末尾结束字符串。例如,如果您在关闭句柄之前检查了字符串的前 10 个字符,并且 GHC 缓冲了额外的 634 个字符,但没有到达文件末尾,那么您将获得一个 644 个字符的普通字符串。这是新用户混淆的常见来源,也是生产代码中偶尔出现的错误来源。
从 GHC 7.10.1 开始,这种行为正在发生变化。当你关闭一个你懒惰读取的句柄时,它现在有效地将异常放在缓冲区的末尾,而不是通常的 :"" .因此,如果您尝试检查超出文件关闭点的字符串,您将收到一条错误消息。

关于haskell - 是什么导致了这个 "delayed read on closed handle"错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26949378/

相关文章:

haskell - 如何在Yesod路由处理程序中使用自定义错误处理程序?

haskell - 为什么默认情况下 forall(RankNTypes 用法)不适用?

lazy-evaluation - hGetContents 是如何实现内存效率的?

Haskell:在惰性 IO 中隐藏故障

haskell - Emacs 组织模式和读写 Haskell

haskell - 在 GHC 手册中记录通配符示例

Haskell - 内置元组,Ord 定义为仅按第一个元素进行比较?

haskell - 类型推断和函数依赖的混淆

haskell - Haskell 中 PETSc FFI 的库设计

Haskell 懒惰 Bytestring 的话不懒惰吗?