hps*_*use 5 haskell exception lazy-evaluation
假设我要打开一个文件并解析其内容,我想懒得这样做:
parseFile :: FilePath -> IO [SomeData]
parseFile path = openBinaryFile path ReadMode >>= parse' where
parse' handle = hIsEOF handle >>= \eof -> do
if eof then hClose handle >> return []
else do
first <- parseFirst handle
rest <- unsafeInterleaveIO $ parse' handle
return (first : rest)
Run Code Online (Sandbox Code Playgroud)
如果在整个阅读过程中没有发生错误,上面的代码就可以了.但是如果抛出异常,就没有机会执行hClose
,并且句柄将无法正确关闭.
通常,如果IO进程不是惰性的,则可以通过catch
或轻松解决异常处理问题bracket
.但是在这种情况下,正常的异常处理方法将导致在实际读取过程开始之前关闭文件句柄.那当然不能接受.
那么,释放一些需要远离其范围的资源的常见方法是因为懒惰,就像我正在做的那样,并且仍然确保异常安全?
openBinaryFile
您可以使用withBinaryFile
:
parseFile :: FilePath -> ([SomeData] -> IO a) -> IO a
parseFile path f = withBinaryFile path ReadMode $ \h -> do
values <- parse' h
f values
where
parse' = ... -- same as now
Run Code Online (Sandbox Code Playgroud)
但是,我强烈建议您考虑使用流数据库,因为它们旨在处理这种情况并正确处理异常。例如,使用管道,您的代码将类似于:
parseFile :: MonadResource m => FilePath -> Producer m SomeData
parseFile path = bracketP
(openBinaryFile path ReadMode)
hClose
loop
where
loop handle = do
eof <- hIsEOF handle
if eof
then return ()
else parseFirst handle >>= yield >> loop handle
Run Code Online (Sandbox Code Playgroud)
如果您重写parseFirst
函数以使用管道本身而不是下拉到Handle
API,则该粘合代码会更短,并且您不会直接绑定到Handle
,这使得使用其他数据源和执行测试变得更容易。
Haskell 学院提供了管道教程。
更新我忘记提及的一件事是,虽然问题集中在阻止文件关闭的异常上,但如果您没有完全消耗输入,即使是非异常情况也会导致这种情况。例如,如果您的文件有多个记录,并且您只强制评估第一个记录,则在垃圾收集器能够回收句柄之前,文件将不会关闭。withBinaryFile
使用流数据库的另一个原因。
归档时间: |
|
查看次数: |
121 次 |
最近记录: |