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错误。
答案 0 :(得分:5)
许多haskellers的懒惰IO is considered to be a pitfall因此建议远离。你的案例很好地描述了原因。
有一个non-lazy alternative of hGetContents
function。它适用于Text
,但Text
也是String
的首选替代方案。为方便起见,有现代前奏曲,将String
替换为Text
:basic-prelude和classy-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)
执行之前实际上不会读取任何内容。