RE: Imperative Object Destruction

2000-11-13 Thread Simon Peyton-Jones

| 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).

Haskell doesn't but GHC has an extension (finalisers) that does.
There's a discussion of the issues in our paper about weak pointers
and finalisers
http://research.microsoft.com/~simonpj/#weak

Simon

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



RE: Imperative Object Destruction

2000-11-13 Thread Ashley Yakeley

At 2000-11-13 00:58, Simon Peyton-Jones wrote:

| 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).

Haskell doesn't but GHC has an extension (finalisers) that does.
There's a discussion of the issues in our paper about weak pointers
and finalisers
   http://research.microsoft.com/~simonpj/#weak

I assume you mean finalisers for ordinary Haskell values after they're no 
longer referenced. That's actually a separate issue, I'm more interested 
in doing finalisation in Haskell's existing monadic imperative model, 
something that shouldn't need any runtime extensions.

-- 
Ashley Yakeley, Seattle WA


___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



RE: Imperative Object Destruction

2000-11-13 Thread Chris Angus

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:[EMAIL PROTECTED]]
 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
 [EMAIL PROTECTED]
 http://www.haskell.org/mailman/listinfo/haskell
 

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Imperative Object Destruction

2000-11-13 Thread Hannah Schroeter

Hello!

On Mon, Nov 13, 2000 at 09:27:07AM -, 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.

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Imperative Object Destruction

2000-11-13 Thread Ashley Yakeley

At 2000-11-13 02:18, Hannah Schroeter wrote:

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

Doesn't fulfill condition 2:

2. no read or write operations are performed on file-handles that have 
not yet been opened, or that have already been closed.

...since you can do

  stealHandle = withOpenFile "filename" ReadMode (\handle -handle)
  stealHandle = read



-- 
Ashley Yakeley, Seattle WA


___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



RE: Imperative Object Destruction

2000-11-13 Thread Ashley Yakeley

At 2000-11-13 01:27, 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.

This is a slight generalisation of my scheme, from which

  withFile :: OpenFile a - String - IO a
  withFile operation name = runOF (openFile name  operation)

How we would do this with 2 files however I'm not so sure.

Looks like you can't.

This might work, however:

  read :: (Int,Int) - OpenFile IO [Byte]
  write :: (Int,[Byte]) - OpenFile IO ()
  withFile :: OpenFile a - String - a

  copyFile :: OpenFile OpenFile IO ()

  withFile (withFile copyFile "dest") "source"

...but I'm not sure how to write copyFile.

-- 
Ashley Yakeley, Seattle WA


___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Imperative Object Destruction

2000-11-13 Thread Hannah Schroeter

Hello!

On Mon, Nov 13, 2000 at 02:30:17AM -0800, Ashley Yakeley wrote:
 [...]

 Doesn't fulfill condition 2:

 2. no read or write operations are performed on file-handles that have 
 not yet been opened, or that have already been closed.

 ...since you can do

   stealHandle = withOpenFile "filename" ReadMode (\handle -  handle)
   stealHandle = read

And that yields an exception, as the handle is definitely closed.

Is there any *real* problem with this?

Kind regards,

Hannah.

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Imperative Object Destruction

2000-11-13 Thread Ashley Yakeley

At 2000-11-13 02:39, Hannah Schroeter wrote:

And that yields an exception, as the handle is definitely closed.

Is there any *real* problem with this?

Well, it doesn't solve the problem I'm interested in. The file-handling 
is just an example, I'd like to solve it for any kind of object 
destruction, so that this kind of error is always caught at compile-time.


-- 
Ashley Yakeley, Seattle WA


___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



RE: Imperative Object Destruction

2000-11-13 Thread Simon Marlow

 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
 
 Doesn't fulfill condition 2:
 
 2. no read or write operations are performed on file-handles 
 that have 
 not yet been opened, or that have already been closed.
 
 ...since you can do
 
   stealHandle = withOpenFile "filename" ReadMode (\handle -  handle)
   stealHandle = read

If you want to enforce this kind of encapsulation using the type system, you
could try using the runST trick, something like:

withOpenFile 
:: FilePath - IOMode 
- (forall s . OpenFile s - IO a) - IO a

of course, this needs the universal quantification extensions implemented in
Hugs  GHC.

Cheers,
Simon

___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell



Re: Imperative Object Destruction

2000-11-13 Thread Marcin 'Qrczak' Kowalczyk

On Mon, 13 Nov 2000, Ashley Yakeley wrote:

 Well, it doesn't solve the problem I'm interested in. The file-handling 
 is just an example, I'd like to solve it for any kind of object 
 destruction, so that this kind of error is always caught at compile-time.

I don't think it's possible without limiting convenience / usefulness.
When an arbitrary IO action may be executed to deal with the object, it
can put it into a mutable reference and then used anywhere, or passed
to another thread, and you've lost control over it.

It can be done in a way similar to the ST monad of GHC and Hugs: tagging
objects and actions with type variables and using a special type for
running the computation which ensures that reference don't escape the
computation (and if they are accessed from elsewhere then they are not
usable there anyway). It restricts doable actions to those provided by the
ST monad - no mixing with arbitrary IO.

-- 
Marcin 'Qrczak' Kowalczyk


___
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell