RE: [Haskell-cafe] Yet another top-level state proposal

2007-05-29 Thread Simon Peyton-Jones
At the risk of becoming repetitious, let's keep refining the Wiki to give these 
competing proposals in their most up-to-date form.  I'm not arguing against 
email -- it's an excellent medium for discussion -- but having the outcomes 
recorded makes them accessible to a much wider audience who have not followed 
the detailed discussion.

Simon

| -Original Message-
| From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On Behalf Of Judah
| Jacobson
| Sent: 28 May 2007 19:50
| To: Adrian Hey
| Cc: haskell-cafe@haskell.org
| Subject: Re: [Haskell-cafe] Yet another top-level state proposal
|
| On 5/26/07, Adrian Hey [EMAIL PROTECTED] wrote:
| 
|  Judah Jacobson wrote:
|   In contrast to recent proposals, this one requires no extra syntax or
|   use of unsafe functions by the programmer.
| 
|  I'm not aware of any proposals (recent or ancient:-) that require the
|  use of unsafe functions by the programmer (well apart from the
|  unsafePerformIO hack itself).
|
| I was referring to the proposal to make that hack somewhat safer by
| adding a NO_INLINE_OR_CSE pragma.
|
|  Also adding extra syntax is no problem
|  these days. It's trivially simple (and indeed desirable in this case
|  IMHO). It's the underlying compiler magic requires significant extra
|  work I think.
|
| Reading last week's conversation on the topic, I got the impression
| that the debate is still ongoing with respect to that point.  Although
| this proposal is a little less aesthetic than those for mdo or ACIO, I
| think the fact that it touches so few parts of the language might make
| some people more comfortable with it.  In particular, an
| implementation only needs to:
|
| - add the OnceInit/OnceIO class declarations (trivial)
| - add the OnceIO deriving clause logic (in GHC, this would be
| isolated to one module)
| - add a NO_CSE pragma at the Core syntax level. (already suggested for
| other conservative proposals).
|
| But whether that's being *too* conservative is a matter of opinion, of course.
|
|   
|   Under this proposal, we would write instead:
|   
|   newtype UniqueRef = UniqueRef (IORef Integer)
|  deriving OnceIO
|  
|   instance OnceInit UniqueRef where
|  onceInit = liftM UniqueRef (newIORef 0)
| 
|  A purely aesthetic objection, but to me it looks quite obfuscated
|  compared to:
| 
|  uniqueRef :: IORef Integer
|  uniqueRef - ACIO.newIORef 0
| 
|  But I guess perhaps what's going on here could be made clearer with
|  the right syntactic sugar :-)
|
| If you're going to use syntactic sugar anyway, I think that negates
| the main appeal of this proposal.  Instead, we could ignore deriving
| clauses altogether, and add an optional keyword oneshot to type
| declarations, e.g.:
|
| oneshot uniqueRef :: IO (IORef Integer)
| uniqueRef = newIORef 0
|
| Now that I mention it, that idea's not too bad either...
|
|  Finally, the useage problem I mentioned. Having to create a distinct
|  type for each top level thing with identity (my terminology)
|  seems like it could cause difficulties (probably not insoluble
|  problems though).
|
| My feeling is that most programs would use few enough TWIs that having
| to declare extra types would not be a big hastle.  But I see you're
| challenging that point below:
|
|  If you look at the wiki page you'll see the device driver example I
|  put there. This has two device handles at the top level (both same
|  type), with a device driver API that takes either the device handle
|  itself or a device session handle (which will contain the corresponding
|  device handle) as parameters (so in principle it can be used with any
|  number of devices provided the corresponding device handles are
|  available).
| 
|  My question is, what would this example look like using the solution
|  you propose? I can think of at least two possibilities, both of which
|  seem quite awkward. But I'll leave it to you to think about this
|  with a bit more care than perhaps I have. It'd be nice to see the
|  solution on the Wiki too.
| 
|
| If you want several different devices, you could wrap them all in one
| large type:
|
| data DeviceHandle = ...
| createDeviceHandle :: BaseAddress - IO DeviceHandle
|
| data AllHandles = AllHandles {handle1, handle2 :: DeviceHandle} deriving 
OnceIO
|
| instance OnceInit AllHandles where
| onceInit = liftM2 AllHandles
|  (createDeviceHandle baseAddress1)
|  (createDeviceHandle baseAddress2)
|
| device1, device2 :: IO DeviceHandle
| device1 = liftM handle1 runOnce
| device2 = liftM handle2 runOnce
|
| This proposal does seem to encourage consolidating TWIs into one part
| of the program; from a design perspective, that may not be entirely a
| bad thing.
|
| Best,
| -Judah
| ___
| Haskell-Cafe mailing list
| Haskell-Cafe@haskell.org
| http://www.haskell.org/mailman/listinfo

Re: [Haskell-cafe] Yet another top-level state proposal

2007-05-29 Thread Claus Reinke



At the risk of becoming repetitious, let's keep refining the Wiki to
give these competing proposals in their most up-to-date form.  I'm not
arguing against email -- it's an excellent medium for discussion -- but
having the outcomes recorded makes them accessible to a much wider
audience who have not followed the detailed discussion.


i would have preferred some discussion here first, but to keep 
it from getting lost, i've now added my own proposal (different
thread, in case you're wondering;) to 


 http://www.haskell.org/haskellwiki/Top_level_mutable_state

as '5 Proposal 4: Shared on-demand IO actions (oneShots)'.

claus

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Yet another top-level state proposal

2007-05-28 Thread Judah Jacobson

On 5/26/07, Adrian Hey [EMAIL PROTECTED] wrote:


Judah Jacobson wrote:
 In contrast to recent proposals, this one requires no extra syntax or
 use of unsafe functions by the programmer.

I'm not aware of any proposals (recent or ancient:-) that require the
use of unsafe functions by the programmer (well apart from the
unsafePerformIO hack itself).


I was referring to the proposal to make that hack somewhat safer by
adding a NO_INLINE_OR_CSE pragma.


Also adding extra syntax is no problem
these days. It's trivially simple (and indeed desirable in this case
IMHO). It's the underlying compiler magic requires significant extra
work I think.


Reading last week's conversation on the topic, I got the impression
that the debate is still ongoing with respect to that point.  Although
this proposal is a little less aesthetic than those for mdo or ACIO, I
think the fact that it touches so few parts of the language might make
some people more comfortable with it.  In particular, an
implementation only needs to:

- add the OnceInit/OnceIO class declarations (trivial)
- add the OnceIO deriving clause logic (in GHC, this would be
isolated to one module)
- add a NO_CSE pragma at the Core syntax level. (already suggested for
other conservative proposals).

But whether that's being *too* conservative is a matter of opinion, of course.


 
 Under this proposal, we would write instead:
 
 newtype UniqueRef = UniqueRef (IORef Integer)
deriving OnceIO

 instance OnceInit UniqueRef where
onceInit = liftM UniqueRef (newIORef 0)

A purely aesthetic objection, but to me it looks quite obfuscated
compared to:

uniqueRef :: IORef Integer
uniqueRef - ACIO.newIORef 0

But I guess perhaps what's going on here could be made clearer with
the right syntactic sugar :-)


If you're going to use syntactic sugar anyway, I think that negates
the main appeal of this proposal.  Instead, we could ignore deriving
clauses altogether, and add an optional keyword oneshot to type
declarations, e.g.:

oneshot uniqueRef :: IO (IORef Integer)
uniqueRef = newIORef 0

Now that I mention it, that idea's not too bad either...


Finally, the useage problem I mentioned. Having to create a distinct
type for each top level thing with identity (my terminology)
seems like it could cause difficulties (probably not insoluble
problems though).


My feeling is that most programs would use few enough TWIs that having
to declare extra types would not be a big hastle.  But I see you're
challenging that point below:


If you look at the wiki page you'll see the device driver example I
put there. This has two device handles at the top level (both same
type), with a device driver API that takes either the device handle
itself or a device session handle (which will contain the corresponding
device handle) as parameters (so in principle it can be used with any
number of devices provided the corresponding device handles are
available).

My question is, what would this example look like using the solution
you propose? I can think of at least two possibilities, both of which
seem quite awkward. But I'll leave it to you to think about this
with a bit more care than perhaps I have. It'd be nice to see the
solution on the Wiki too.



If you want several different devices, you could wrap them all in one
large type:

data DeviceHandle = ...
createDeviceHandle :: BaseAddress - IO DeviceHandle

data AllHandles = AllHandles {handle1, handle2 :: DeviceHandle} deriving OnceIO

instance OnceInit AllHandles where
   onceInit = liftM2 AllHandles
(createDeviceHandle baseAddress1)
(createDeviceHandle baseAddress2)

device1, device2 :: IO DeviceHandle
device1 = liftM handle1 runOnce
device2 = liftM handle2 runOnce

This proposal does seem to encourage consolidating TWIs into one part
of the program; from a design perspective, that may not be entirely a
bad thing.

Best,
-Judah
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Yet another top-level state proposal

2007-05-26 Thread Isaac Dupree
-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1

Judah Jacobson wrote:
 Hi all,
 
 Given the recent discussion about adding top-level mutable state to
 Haskell, I thought it might be a good time to throw my own proposal
 into the ring.  If enough people think it's worth considering, I can
 add it to the wiki page.
 (http://www.haskell.org/haskellwiki/Top_level_mutable_state)
 
 In contrast to recent proposals, this one requires no extra syntax or
 use of unsafe functions by the programmer.  Any nonstandard magic
 that might occur is kept within the compiler internals.  Furthermore,
 top-level initializations are only executed when needed; merely
 importing a module does not cause any additional actions to be run at
 startup.
 
 The core idea, similar to that of type-based execution contexts on
 the above wiki page, is to associate each top-level action with its
 own type.

I like the idea, similar to deriving(Typeable) in creating uniqueness.
Hopefully it can be made obvious somehow to code-readers unfamiliar with
it that the code is doing something unusual/controversial...

Isaac

(P.S. having read the Top_level_mutable_state page now, it is definitely
worth reading)
-BEGIN PGP SIGNATURE-
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGWB+6HgcxvIWYTTURAlu0AKCR7eIm9NQjnpUt4PbYhaIJylWgPwCgnZul
JB4jszUNnd8+2HO+cR9LW7U=
=RxW5
-END PGP SIGNATURE-
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Yet another top-level state proposal

2007-05-26 Thread Felipe Almeida Lessa

On 5/26/07, Isaac Dupree [EMAIL PROTECTED] wrote:

(P.S. having read the Top_level_mutable_state page now, it is definitely
worth reading)


I'm a newbie in Haskell, but for me this seems to be the better
proposal until now. It's definitely worth adding to the wiki, at
least.

--
Felipe.
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Yet another top-level state proposal

2007-05-26 Thread Adrian Hey

Hello,

It's nice to see someone else is seriously thinking about this and
I can't see anything obviously wrong with this on first inspection so
by all means add it to the wiki. I have a few issues with it on
aesthetic grounds though. There also seems to be a bit of problem with
using this solution in practice (I'll mention this at the end).

Judah Jacobson wrote:

In contrast to recent proposals, this one requires no extra syntax or
use of unsafe functions by the programmer.


I'm not aware of any proposals (recent or ancient:-) that require the
use of unsafe functions by the programmer (well apart from the
unsafePerformIO hack itself). Also adding extra syntax is no problem
these days. It's trivially simple (and indeed desirable in this case
IMHO). It's the underlying compiler magic requires significant extra
work I think.



Under this proposal, we would write instead:

newtype UniqueRef = UniqueRef (IORef Integer)
   deriving OnceIO

instance OnceInit UniqueRef where
   onceInit = liftM UniqueRef (newIORef 0)


A purely aesthetic objection, but to me it looks quite obfuscated
compared to:

uniqueRef :: IORef Integer
uniqueRef - ACIO.newIORef 0

But I guess perhaps what's going on here could be made clearer with
the right syntactic sugar :-)

Finally, the useage problem I mentioned. Having to create a distinct
type for each top level thing with identity (my terminology)
seems like it could cause difficulties (probably not insoluble
problems though).

If you look at the wiki page you'll see the device driver example I
put there. This has two device handles at the top level (both same
type), with a device driver API that takes either the device handle
itself or a device session handle (which will contain the corresponding
device handle) as parameters (so in principle it can be used with any
number of devices provided the corresponding device handles are
available).

My question is, what would this example look like using the solution
you propose? I can think of at least two possibilities, both of which
seem quite awkward. But I'll leave it to you to think about this
with a bit more care than perhaps I have. It'd be nice to see the
solution on the Wiki too.

Regards
--
Adrian Hey


___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] Yet another top-level state proposal

2007-05-25 Thread Judah Jacobson

Hi all,

Given the recent discussion about adding top-level mutable state to
Haskell, I thought it might be a good time to throw my own proposal
into the ring.  If enough people think it's worth considering, I can
add it to the wiki page.
(http://www.haskell.org/haskellwiki/Top_level_mutable_state)

In contrast to recent proposals, this one requires no extra syntax or
use of unsafe functions by the programmer.  Any nonstandard magic
that might occur is kept within the compiler internals.  Furthermore,
top-level initializations are only executed when needed; merely
importing a module does not cause any additional actions to be run at
startup.

The core idea, similar to that of type-based execution contexts on
the above wiki page, is to associate each top-level action with its
own type.  For example, the current way to declare a source for unique
integers is:


{-# NOINLINE uniqueRef #-}
uniqueRef :: IORef Integer
uniqueRef = unsafePerformIO $ newIORef 0

uniqueInt :: IO Integer
uniqueInt = do
   n - readIORef uniqueRef
   writeIORef uniqueRef (n+1)
   return n

Under this proposal, we would write instead:

newtype UniqueRef = UniqueRef (IORef Integer)
   deriving OnceIO

instance OnceInit UniqueRef where
   onceInit = liftM UniqueRef (newIORef 0)

uniqueInt :: IO Integer
uniqueInt = do
   UniqueRef uniqueRef - runOnceIO
   n - readIORef uniqueRef
   writeIORef uniqueRef (n+1)
   return n


The above code uses two classes:

class OnceInit a where
   onceInit :: IO a

class OnceInit a = OnceIO a where
   runOnceIO :: IO a

The OnceInit class lets the programmer to specify how a type is
initialized; above, it just allocates a new IORef, but we could also
read a configuration file or parse command-line arguments, for
example.

In contrast, instances of the OnceIO class are not written by the
programmer; instead, they are generated automatically by a deriving
OnceIO clause.Each type for which OnceIO is
derived will have a special top-level action associated with it, which
is accessed through the runOnceIO function.  Its semantics are:

- The first time that runOnceIO is called, it runs the corresponding
onceInit action and caches and returns the result.
- Every subsequent time that runOnceIO is called, it returns the
cached result.

This behavior is safe precisely because runOnceIO is an IO action.
Even though one can't guarantee when in the program an initialization
will occur, when the initialization does happen it will be sequenced
among other IO actions.

To illustrate this behavior, here are a couple sample implementations
in plain Haskell.  These do use unsafePerformIO, but in practice any
such details would be hidden in the instance derived by the compiler
(along with any related NOINLINE/NOCSE pragmas):

instance Once UniqueRef where
   runOnceIO = return $! unsafePerformIO onceInit

or (less efficient, but multithreaded safe):

instance Once UniqueRef where
   runOnceIO = modifyMVar onceUniqueRef $ \mx - case mx of
   Just x - return (Just x, x)
   Nothing - do {x - onceInit; return (Just x, x)}

onceUniqueRef = unsafePerformIO $ newMVar Nothing

Finally, note that the deriving clause can easily check whether the
type in question is monomorphic (as is necessary for type-safety),
since it already has access to the type definition.


Anyway, that's the gist of my proposal; I hope I've explained it well,
but please let let me know if you have questions, suggestions or
criticisms.

Best,
-Judah
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe