RE: Imperative Object Destruction
why not create an abstract datatype OpenFile a which is a monad data OpenFile a = OpenFile (Maybe FileHandle -> IO a) and create you operations in terms of this openFile :: String -> OpenFile () count :: OpenFile Int read :: Int -> OpenFile [Byte] then you could habe a run function runOF :: OpenFile a -> IO a which ran whatever you wanted for the file and ensured tahthe file was closed correctly afterwards. How we would do this with 2 files however I'm not so sure.
-----Original Message----- From: Ashley Yakeley [mailto:ashley@semantic.org] Sent: 13 November 2000 07:21 To: Haskell List Subject: Imperative Object Destruction
C++ provides a convenient mechanism for cleaning up stuff, the destructor, which is guaranteed to get called when an object passes out of scope (or something).
I'm wondering how to make a Haskell equivalent for imperative code. For instance, consider a simple file API, with these operations:
open: given a string (the filename), open an existing file with that name, return a file-handle (or fail)
count: given a file-handle, get the length of the file
read: given a file-handle, offset into the file and a number of bytes, read the file returning a list of bytes (or fail)
write: given a file-handle, offset into the file and a list of bytes, write the bytes to the file at the offset
close: given a file-handle, close the handle
In C++ I could simply create an OpenFile class, with the close operation in the destructor. The actual operations would be hidden from OpenFile's clients.
I'd like to represent this API in Haskell so that the type-rules guarantee that in any imperative action of type 'IO a',
1. every opened file gets closed exactly once
2. no read or write operations are performed on file-handles that have not yet been opened, or that have already been closed.
My first guess was to create a function that encapsulated the entire life-cycle of the file, passing in a function that would represent whatever one wished to do on the file:
count :: Handle -> IO Integer read :: (Integer,Integer) -> Handle -> IO [Byte] write :: (Integer,[Byte]) -> Handle -> IO () withFile :: (Handle -> IO a) -> String -> IO a
'withFile operation name' would open the file called 'name', perform the operation 'operation', and then close the file. So for instance, to get the length of a file named "/home/ashley/foo":
withFile count "/home/ashley/foo"
Trouble is, one can easily pass a return function to withFile to get ahold of the handle after it's been closed.
My second guess was to use a special type to represent an imperative operation that needed a handle:
read :: (Integer,Integer) -> HandleOperation [Byte] write :: (Integer,[Byte]) -> HandleOperation () withFile :: HandleOperation a -> String -> IO a
Of course, I'd then need to provide functions to compose/concatenate HandleOperation values. But I can't help thinking this problem is already well-known and there's a straightforward solution...
-- Ashley Yakeley, Seattle WA
_______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
Hello! On Mon, Nov 13, 2000 at 09:27:07AM -0000, Chris Angus wrote:
why not create an abstract datatype
OpenFile a which is a monad
data OpenFile a = OpenFile (Maybe FileHandle -> IO a)
and create you operations in terms of this
openFile :: String -> OpenFile () count :: OpenFile Int read :: Int -> OpenFile [Byte]
then you could habe a run function
runOF :: OpenFile a -> IO a
which ran whatever you wanted for the file and ensured tahthe file was closed correctly afterwards. How we would do this with 2 files however I'm not so sure.
[...]
That's the problem. And I think your solution is overly complicated. Why not copy what Common Lisp does, just that Haskell can do it without macros: withOpenFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a withOpenFile name mode action = do handle <- openFile name mode result <- (action handle) `finally` (hClose handle) return result Usage: count :: Handle -> IO Int read :: Handle -> IO [Byte] fileLength <- withOpenFile "filename" ReadMode $ \handle -> count handle fileContent <- withOpenFile "filename" ReadMode $ \handle -> read handle or even, if you want both results without opening the file twice: rewind :: Handle -> IO () (fileLength, fileContent) <- withOpenFile "filename" ReadMode $ \handle -> do len <- count handle rewind handle cont <- read handle return (len, cont) Two files: result <- withOpenFile "file1" ReadMode $ \handle1 -> withOpenFile "file2" ReadMode $ \handle2 -> ... Kind regards, Hannah.
participants (2)
-
Chris Angus -
Hannah Schroeter