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