他の人が述べているように、それは遅延評価のためです。この操作の後、ハンドルは半分閉じられ、すべてのデータが読み取られると自動的に閉じられます。このように、hGetContents と readFile はどちらも怠惰です。ハンドルを開いたままにしておくことに問題がある場合は、通常、読み取りを強制します。簡単な方法は次のとおりです。
import Control.Parallel.Strategies (rnf)
-- rnf means "reduce to normal form"
main = do inFile <- openFile "foo"
contents <- hGetContents inFile
rnf contents `seq` hClose inFile -- force the whole file to be read, then close
putStr contents
しかし最近では、ファイル I/O に文字列を使用する人はもういません。新しい方法は、Data.ByteString (ハックで利用可能) と、遅延読み取りが必要な場合に Data.ByteString.Lazy を使用することです。
import qualified Data.ByteString as Str
main = do contents <- Str.readFile "foo"
-- readFile is strict, so the the entire string is read here
Str.putStr contents
ByteStrings は、大きな文字列 (ファイルの内容など) を処理する方法です。これらは、文字列 (= [Char]) よりもはるかに高速で、メモリ効率が高くなります。
ノート:
便宜上、Control.Parallel.Strategies から rnf をインポートしました。あなたはそれのようなものを自分でかなり簡単に書くことができます:
forceList [] = ()
forceList (x:xs) = forceList xs
これは、リストの背骨 (値ではなく) のトラバーサルを強制するだけで、ファイル全体を読み取る効果があります。
レイジー I/O は、専門家によって悪と見なされるようになっています。当分の間、ほとんどのファイル I/O に厳密なバイト文字列を使用することをお勧めします。オーブンには、構成可能な増分読み取りを取り戻そうとするいくつかのソリューションがあり、その中で最も有望なものは、Oleg による「Iteratee」と呼ばれています。