从hGetContents中检测惰性字符串中的I / O异常?

时间:2013-04-27 21:28:11

标签: haskell error-handling

hGetContents返回一个惰性String对象,该对象可用于纯函数代码以从文件句柄中读取。如果在读取此惰性字符串时发生I / O异常,则会以静默方式关闭基础文件句柄,并且不会向惰性字符串添加其他字符。

如何检测此I / O异常?

作为一个具体的例子,请考虑以下程序:

import System.IO    -- for stdin

lengthOfFirstLine :: String -> Int
lengthOfFirstLine "" = 0
lengthOfFirstLine s  = (length . head . lines) s

main :: IO ()
main = do
    lazyStdin <- hGetContents stdin
    print (lengthOfFirstLine lazyStdin)

如果在读取文件的第一行时发生异常,则此程序将打印字符数,直到发生I / O异常。相反,我希望程序崩溃并使用适当的I / O异常。如何修改此程序以使其具有该行为?

编辑:仔细检查hGetContents实现后,似乎不会忽略I / O异常,而是通过调用纯函数代码冒泡到任何触发评估的IO代码,哪有机会再处理它。 (我以前没有意识到纯函数代码会引发异常。)因此,这个问题是一个误解。

旁白:如果通过经验验证这种特殊行为,那将是最好的。不幸的是,很难模拟低级别的I / O错误。

2 个答案:

答案 0 :(得分:5)

许多haskellers的懒惰IO is considered to be a pitfall因此建议远离。你的案例很好地描述了原因。

有一个non-lazy alternative of hGetContents function。它适用于Text,但Text也是String的首选替代方案。为方便起见,有现代前奏曲,将String替换为Textbasic-preludeclassy-prelude

答案 1 :(得分:1)

  

除此之外:如果验证了这种特殊行为,那将是最好的   凭经验。不幸的是,很难模拟低级I / O.   错误。

我想知道同样的事情,发现这个老问题,并决定进行实验。

我在Windows中运行这个小程序,它监听连接并从懒惰地读取它:

xyz_id

在Linux机器上,我使用import System.IO import Network import Control.Concurrent main :: IO () main = withSocketsDo (do socket <- listenOn (PortNumber 19999) print "created socket" (h,_,_) <- accept socket print "accepted connection" contents <- hGetContents h print contents) 打开了一个连接:

nc

然后使用Windows Sysinternal的TCPView实用程序强行关闭连接。结果是:

nc -v mymachine 19999
Connection to mymachine 19999 port [tcp/*] succeeded!

看来I / O异常会冒泡。

进一步的实验:我在Main.exe: <socket: 348>: hGetContents: failed (Unknown error) 电话后添加了延迟:

hGetContents

通过此更改,终止连接不会立即引发异常,因为由于延迟I / O,在... contents <- hGetContents h threadDelay (60 * 1000^2) print contents) 执行之前实际上不会读取任何内容。