
| -----Original Message----- | From: Ross Paterson [mailto:ross@soi.city.ac.uk] | Sent: 18 September 2003 10:20 | To: Simon Peyton-Jones | Cc: Alastair Reid; Simon Marlow; libraries@haskell.org | Subject: Re: RealWorld | | On Thu, Sep 18, 2003 at 08:56:40AM +0100, Simon Peyton-Jones wrote: | > Is the proposal that the library design exposes to the programmer the | > following type synonyms: | > | > type IO a = ST IORegion a | > type IORef a = STRef IORegion a | | The proposal is to expose | | type IORef = STRef IORegion | type IOArray = STArray IORegion | type IOUArray = STUArray IORegion | | (modulo the name of IORegion) but to keep IO opaque. I think that while | IO includes state, it is not itself a state monad (because the "state" | there can be changed by other agents at the same time). Aha. I hadn't realised that you want to expose IORef as an STRef, but not the IO monad itself. I certainly don't object to that, but I'm not sure of all the implications. For example, at the moment one could have class MRef m r | m -> r, r -> m where new :: a -> m (r a) read :: r a -> m a write :: r a -> a -> m () instance MRef IO IORef where ... instance MRef (ST s) (STRef s) where ... I don't think that'll be legal if IORef = STRef IORegion, because the "r->m" dependency becomes ambiguous. So we'd have to lose the "r->m" dependency. Maybe that's ok. (I don't think MRef currently exists in the libraries.) But it's an example of a consequence of the choice you suggest. Simon | | > I think it is sound to do so. The advantage would be that newIORef and | > newSTRef would be the same (newRef, perhaps); ditto readRef, writeRef | > etc. | | If IO is opaque, then we have | | newIORef = stToIO . newSTRef | | etc. It's not quite as generic as your version, but it is a conceptual | simplification, and it does allow generic classes like | | class MonadST s m | m -> s where | liftST :: Control.Monad.ST.ST s a -> m a | | with instances for IO, both ST's and various constructed monads, so we | could have the likes of | | readSTRef' :: MonadST s m => STRef s a -> m a | readSTRef' r = liftST (Data.STRef.readSTRef r) | | My problem with exposing the GHC implementation of IO is that I don't | think it's the Truth, and Haskell has a record of punishing untruths | sooner or later. | | > I think this would be fine for GHC. It might not be fine for Hugs | > because they implement IO and ST differently I think. No idea about | > NHC. | | NHC doesn't have ST (rank-2 types). In Hugs, they are now | | newtype IO a = IO ((a -> IOResult) -> IOResult) | newtype ST s a = ST (forall r. (a -> r) -> r) | | primitive newIORef "newRef" :: a -> IO (IORef a) | primitive newSTRef "newRef" :: a -> ST s (STRef s a) | | But I'm not proposing that this be exposed. | | > Or, alternatively, is the question something to do with the internals of | > the library, of interest to implementers but not to users of the | > library? Or is it just to do with the type of stToIO, which should be | > | > stToIO :: ST IORegion a -> IO a | | It's more a matter of making IORegion work a bit harder since we have to | put it in the interface anyway.

On Thu, Sep 25, 2003 at 02:30:45PM +0100, Simon Peyton-Jones wrote:
Ross Paterson writes: | The proposal is to expose | | type IORef = STRef IORegion | type IOArray = STArray IORegion | type IOUArray = STUArray IORegion | | (modulo the name of IORegion) but to keep IO opaque. I think that while | IO includes state, it is not itself a state monad (because the "state" | there can be changed by other agents at the same time).
(where IORegion occurs in stToIO :: ST IORegion a -> IO a to make it safe)
Aha. I hadn't realised that you want to expose IORef as an STRef, but not the IO monad itself.
I certainly don't object to that, but I'm not sure of all the implications. For example, at the moment one could have class MRef m r | m -> r, r -> m where new :: a -> m (r a) read :: r a -> m a write :: r a -> a -> m ()
instance MRef IO IORef where ... instance MRef (ST s) (STRef s) where ...
I don't think that'll be legal if IORef = STRef IORegion, because the "r->m" dependency becomes ambiguous. So we'd have to lose the "r->m" dependency.
The reason for having an MRef class was that there were two reference types. If there's only one, but parameterized by region, you might as well parameterize on the region instead, describing a class of monads that contain a heap region: class MonadST s m | m -> s where liftST :: Control.Monad.ST.ST s a -> m a Then you can't have the s->m dependency: instance MonadST s (Control.Monad.ST.ST s) where liftST = id instance MonadST s (Control.Monad.ST.Lazy.ST s) where liftST = strictToLazyST instance MonadST IORegion IO where liftST = Control.Monad.ST.stToIO instance MonadST s m => MonadST s (ContT r m) where liftST = lift . liftST ... but you don't want it: you want to manipulate the same references in different monads (as we already do with the lazy and strict ST monads). All the operations lift to all such monads, e.g. read :: MonadST s m => STRef s a -> m a read r = liftST (Data.STRef.readSTRef r) and that handles arrays as well as refs.
participants (2)
-
Ross Paterson
-
Simon Peyton-Jones