[Haskell-cafe] letting go of file handles and Data.Binary

Ben midfield at gmail.com
Sun Apr 20 20:36:23 EDT 2008


one more piece of email pollution:

import Control.Exception (bracket)
import System.IO
import Data.Binary
import qualified Data.ByteString.Lazy as B

strictDecodeFile :: Binary a => FilePath -> IO a
strictDecodeFile path  =
    bracket (openBinaryFile path ReadMode) hClose $ \h -> do
      c <- B.hGetContents h
      return $! decode c

seems to work like Data.Binary.decodeFile but explicitly closes the handle.

take care, ben

On Sun, Apr 20, 2008 at 4:35 PM, Ben <midfield at gmail.com> wrote:
> FWIW, installed bytestring-0.9.1.0, ran ghc-pkg hide
>  bytestring-0.9.0.1, recompiled and reinstalled binary-0.4.1.  then i
>  played around with all that you suggested, and came to the conclusion
>  that i don't understand seq!
>
>  import Control.Exception (bracket)
>  import System.Directory
>  import System.IO
>  import Data.Binary
>  import Data.ByteString.Lazy as B
>
>
>  strictDecodeFile :: Binary a => FilePath -> (a -> b) -> IO ()
>  strictDecodeFile path force =
>     bracket (openFile path ReadMode) hClose $ \h -> do
>       c <- B.hGetContents h
>       force (decode c) `seq` return ()
>
>  strictDecodeFile' :: Binary a => FilePath -> (a -> IO b) -> IO ()
>
> strictDecodeFile' path force =
>     bracket (openFile path ReadMode) hClose $ \h -> do
>       c <- B.hGetContents h
>       force (decode c)
>       return ()
>
>
>  main = do
>   let dat = [1..10]::[Int]
>       fname = "foo.dat"
>   encodeFile fname dat
>   h <- openFile fname ReadMode
>   c <- B.hGetContents h
>   let dat2 = decode c
>   print (dat == dat2)
>   hClose h
>   removeFile fname
>
>   encodeFile fname dat
>   strictDecodeFile fname (\x -> do print "strict 1"
>                                    print (x == dat))
>   removeFile fname
>
>   encodeFile fname dat
>   strictDecodeFile' fname (\x -> do print "strict 2"
>                                     print (x == dat))
>   removeFile fname
>
>   encodeFile fname dat
>   dat4 <- decodeFile fname
>   print (dat == dat4)
>   removeFile fname
>
>  running main outputs
>
>  True
>  "strict 2"
>  True
>  True
>  *** Exception: foo.dat: removeFile: permission denied (Permission denied)
>
>  e.g. the handle version works, Bryan's original strictDecodeFile
>  appears to not run "force", the modified strictDecodeFile' does run
>  "force" (i didn't use seq, just an additional line in the monad), and
>  the encodeFile / decodeFile / removeFile appears to still not work
>  with the latest bytestring.  what's the difference between the seq and
>  non-seq versions?
>
>  for now i can use strictDecodeFile' but at least something should be
>  said in the docs about decodeFile et al holding handles.  (i
>  understand this is not the fault of binary per se as much as haskell's
>  non-strict semantics, but a reminder for noobs like me would be
>  helpful.)  and finally something like strictDecodeFile' might be
>  useful in the library?
>
>  thanks for the help, ben
>
>
>
>  On Sun, Apr 20, 2008 at 2:34 PM, Duncan Coutts
>  <duncan.coutts at worc.ox.ac.uk> wrote:
>  >
>  >  On Sun, 2008-04-20 at 14:24 -0700, Bryan O'Sullivan wrote:
>  >  > Doh!  For all that I wrote about encodeFile, substitute decodeFile.
>  >
>  >
>  >  Indeed the version of encodeFile you wrote should be exactly identical
>  >  to the original because the lazy bytestring writeFile already uses
>  >  bracket like that:
>  >
>  >  writeFile :: FilePath -> ByteString -> IO ()
>  >  writeFile f txt = bracket (openBinaryFile f WriteMode) hClose
>  >     (\hdl -> hPut hdl txt)
>  >
>  >
>  >  > strictDecodeFile :: Binary a => FilePath -> (a -> b) -> IO ()
>  >  > strictDecodeFile path force =
>  >  >     bracket (openFile path ReadMode) hClose $ \h -> do
>  >  >       c <- L.hGetContents h
>  >  >       force (decode c) `seq` return ()
>  >
>  >  Yes, the problem with Ben's program was that decodeFile is lazily
>  >  reading the file and lazily decoding. If the decoding consumes all the
>  >  input then it should be possible to avoid rewriting decodeFile and use:
>  >
>  >   dat2 <- decodeFile fname
>  >   evaluate dat2
>  >   removeFile fname
>  >
>  >  It's not immediately clear to me if we can make the decodeFile behave
>  >  like your version. I'd have to go think about whether running the Get
>  >  monad can lazily return values or if it always consumes all the input
>  >  it'll ever need.
>  >
>  >  Duncan
>  >
>  >
>


More information about the Haskell-Cafe mailing list