If you want to use a file in Haskell, you have two options. You can open and close the file yourself:
openFile :: FilePath -> IOMode -> IO Handle hClose :: Handle -> IO ()
… or you can use a function that takes care of the open-close lifecycle:
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
Which is the better interface? It’s a trade-off: withFile
guarantees that every open file is closed, while openFile
and hClose
allow you to open and close files in any order.
Let’s create types to represent these two kinds of interface:
type OpenClose h = IO (h,IO ()) -- opens a resource, returns a handle and a "closer" type With h = forall r. (h -> IO r) -> IO r openCloseFile :: FilePath -> IOMode -> OpenClose Handle openCloseFile path mode = do h <- openFile path mode return (h, hClose h) withFile :: FilePath -> IOMode -> With Handle -- already defined
Is it possible to convert between the two? Here’s how to obtain a With
interface from an OpenClose
interface:
openCloseToWith :: OpenClose h -> With h openCloseToWith oc f = do (h, closer) <- oc finally (f h) closer
It’s also possible to obtain an OpenClose
interface from a With
interface. Can you see how?
withToOpenClose :: With h -> OpenClose h withToOpenClose = ...?
— Ashley Yakeley