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



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 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 Ashley Yakeley

At 2000-11-13 02:30, Ashley Yakeley wrote:

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

Sorry, that should read

  stealHandle = withOpenFile "filename" ReadMode return
  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 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 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 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 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 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 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 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