Закрытие дескриптора файла вызывает проблемы в Haskell

#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