#haskell #io
Вопрос:
У меня есть следующий код для загрузки содержимого файла и его анализа с помощью aeson в объект:
loadFromFile filename = do
fileHandle <- openFile ( dataDir "/" filename ) ReadMode
contents <- B.hGetContents fileHandle
let result = fromMaybe initial $! JSON.decode contents in do
hClose fileHandle
return $! result
Проблема с этим кодом заключается в том, что он вызывает следующие ошибки:
filename.json: hGetBufSome: illegal operation (handle is closed)
filename.json: hGetBufSome: illegal operation (handle is closed)
Я не совсем понимаю, почему он думает, что дескриптор файла закрыт. В конце концов, я читаю его перед тем, как закрыть ручку. Или ленивая оценка влияет на порядок выполнения? Я попытался добавить соответствующие $!
операторы, чтобы ускорить его оценку, но это не помогает. Если я удалю hClose fileHandle
строку, то начну получать спорадические ошибки следующего рода:
filename.json: openFile: resource busy (file is locked)
Если кто-нибудь знает, как исправить эту ошибку 22, я был бы очень признателен. Спасибо!
Комментарии:
1.
hGetContents
переводит дескриптор в полузакрытое состояние и использует спорный «ленивый ввод-вывод». Это означает, что файл не читается в этот момент, но будет прочитан, когдаcontents
это действительно потребуется, при условии, что файл не был закрыт. Ленивый ввод-вывод, как известно, трудно контролировать, и это приведет к ошибкам, о которых вы сообщаете. Может быть, попробуешьevaluate result
, прежде чем закроешь ручку.
Ответ №1:
Вместо того, чтобы форсировать результат после закрытия дескриптора, вы должны форсировать результат перед закрытием дескриптора, вот как вы можете это сделать с помощью BangPatterns
:
loadFromFile filename = do
fileHandle <- openFile ( dataDir "/" filename ) ReadMode
contents <- B.hGetContents fileHandle
let !result = fromMaybe initial $! JSON.decode contents
hClose fileHandle
return result
В этом случае deepseq не требуется, потому что (я полагаю) decode
функция обработает весь ввод , прежде чем решить, возвращать Nothing
или Just ...
, поэтому достаточно форсировать только один уровень.
И, если возможно, лучше полностью избегать ручек:
loadFromFile filename = do
contents <- B.readFile ( dataDir "/" filename )
return $! fromMaybe initial $! JSON.decode contents