
(Moving to Haskell cafe) Edward Kmett wrote:
On Sun, Aug 24, 2008 at 7:12 PM, Ashley Yakeley
wrote: Is there any interest in implementing a top level "<-" to run monadic code?
This is actually implemented in jhc. See the 'top level actions' section of http://repetae.net/computer/jhc/manual.html
Gosh! I was always quite impressed by Johns determination to write a Haskell compiler and by his self discipline in resisting the temptation to fix everything that was wrong with Haskell and keep to standards :-) I implemented my own ACIO monad a while ago (which is of course quite useless without top level <- bindings) and it turned out that there was quite a lot that could go in here. The only problem seemed to be that some things that seemed perfectly reasonable to create via ACIO had *IO* finalisers associated with them, which didn't feel right to me. But if you think about how finalisers get run I'm inclined to think we should insist that they are ACIO too. Regards -- Adrian Hey

On Mon, Aug 25, 2008 at 04:55:05PM +0100, Adrian Hey wrote:
(Moving to Haskell cafe)
Edward Kmett wrote:
On Sun, Aug 24, 2008 at 7:12 PM, Ashley Yakeley
wrote: Is there any interest in implementing a top level "<-" to run monadic code?
This is actually implemented in jhc. See the 'top level actions' section of http://repetae.net/computer/jhc/manual.html
Gosh! I was always quite impressed by Johns determination to write a Haskell compiler and by his self discipline in resisting the temptation to fix everything that was wrong with Haskell and keep to standards :-)
Heh. :) I have not had much time recently to work on jhc. but I hope to pick it up full steam soon. There have been a lot of strong contributers in the community as well. (sigh. day job syndrome).
I implemented my own ACIO monad a while ago (which is of course quite useless without top level <- bindings) and it turned out that there was quite a lot that could go in here.
Yeah, I based my design on what I thought was the 'community consensus' on how they should look the last time the discussion came up. (not counting those that were adamantly opposed to the idea alltogether ;) ) I forgot who came up with the original ACIO idea, but I'd give them props in the manual if they wish.
The only problem seemed to be that some things that seemed perfectly reasonable to create via ACIO had *IO* finalisers associated with them, which didn't feel right to me. But if you think about how finalisers get run I'm inclined to think we should insist that they are ACIO too.
Yeah, this sounds like a great idea. there were a whole lot of issues dealing with finalizers and concurrency, and restricting them in some way similar to ACIO might be good... however, you want something a little weaker than ACIO I think. it must satisfy the ACIO conditions, but _may_ assume its argument (the item being collected) is never referenced again. hence something like 'free' is okay which wouldn't be if other references to the object exist. do you think that is 'formal' enough of a description? seems clear enough if ACIO is well defined which I think it is. John -- John Meacham - ⑆repetae.net⑆john⑈

John Meacham wrote:
... if ACIO is well defined which I think it is.
Affine: ma >> mb = mb Central: do {a <- ma;b <- mb;return (a,b)} = do {b <- mb;a <- ma;return (a,b)} ...for some concept of "=". Is this correct? Are there any other constraints?

John Meacham wrote:
I forgot who came up with the original ACIO idea, but I'd give them props in the manual if they wish.
I think this is based on Ian Starks message.. http://www.haskell.org/pipermail/haskell-cafe/2004-November/007664.html
Yeah, this sounds like a great idea. there were a whole lot of issues dealing with finalizers and concurrency, and restricting them in some way similar to ACIO might be good... however, you want something a little weaker than ACIO I think. it must satisfy the ACIO conditions, but _may_ assume its argument (the item being collected) is never referenced again. hence something like 'free' is okay which wouldn't be if other references to the object exist. do you think that is 'formal' enough of a description? seems clear enough if ACIO is well defined which I think it is.
Yes, now I cast my mind back that was something that was troubling me. Clearly the one thing you're most likely to want to do in a finaliser is free some external resource, which certainly wouldn't be ACIO ordinarily. But as you say, giving sane semantics and type safety to finalisers is very tricky indeed. I can't help thinking that semantically finaliser execution should be treated like some kind of external event handling, like an interrupt. Not sure what that should be properly, but I think finalisers should be the same. But from a top level aThing <- someACIO point of view, if we're going to say that it doesn't matter if someACIO is executed before main is entered (possibly even at compile time) or on demand, then we clearly don't want to observe any difference between the latter case and the former (if aThing becomes garbage without ever being demanded). Maybe it would be safest to just say anything with a finaliser can't be created at the top level. We can always define an appropriate top level "get" IO action using runOnce or whatever. Regards -- Adrian Hey

On Tue, Aug 26, 2008 at 12:07 AM, Adrian Hey
But from a top level aThing <- someACIO point of view, if we're going to say that it doesn't matter if someACIO is executed before main is entered (possibly even at compile time) or on demand, then we clearly don't want to observe any difference between the latter case and the former (if aThing becomes garbage without ever being demanded).
Maybe it would be safest to just say anything with a finaliser can't be created at the top level. We can always define an appropriate top level "get" IO action using runOnce or whatever.
I've been wondering: is there any benefit to having top-level ACIO'd <- instead of just using runOnce (or perhaps "oneshot") as the primitive for everything? For example: oneshot uniqueRef :: IO (MVar Integer) uniqueRef =< newMVar 0 It was also suggested in that wiki page: http://haskell.org/haskellwiki/Top_level_mutable_state#Proposal_4:_Shared_on... Those proposals eliminate the need for creating an ACIO monad and enforcing its axioms, since one-shot actions are executed in-line with other I/O actions (rather than at some nebulous "before the program is run" time). So, in the context of top-level initializers, does ACIO offer something beyond what oneshot provides on its own? If not, I prefer the latter since it seems like a much simpler solution. Best, -Judah

Judah Jacobson wrote:
I've been wondering: is there any benefit to having top-level ACIO'd <- instead of just using runOnce (or perhaps "oneshot") as the primitive for everything?
I don't think oneshots are very good for open witness declarations (such as the open exceptions I mentioned originally), since there are pure functions that do useful things with them. Not sure about TVars either, which operate in the STM monad. Would you also need a oneshotSTM (or a class)? -- Ashley Yakeley

On Tue, Aug 26, 2008 at 3:15 AM, Ashley Yakeley
Judah Jacobson wrote:
I've been wondering: is there any benefit to having top-level ACIO'd <- instead of just using runOnce (or perhaps "oneshot") as the primitive for everything?
I don't think oneshots are very good for open witness declarations (such as the open exceptions I mentioned originally), since there are pure functions that do useful things with them.
I think you're saying that you want to write "w <- newIOWitness" at the top level, so that w can then be referenced in a pure function. Fair enough. But newIOWitness's implementation requires writeIORef (or an equivalent), which is not ACIO, right? I suppose you could call unsafeIOToACIO, but if that function is used often it seems to defeat the purpose of defining an ACIO monad in the first place.
Not sure about TVars either, which operate in the STM monad. Would you also need a oneshotSTM (or a class)?
Interesting point; I think you can work around it, but it does make the code a little more complicated. For example: oneshot uniqueVar :: IO (TVar Integer) uniqueVar =< atomically $ newTVar 0 -- alternately, use newTVarIO uniqueIntSTM :: IO (STM Integer) uniqueIntSTM = uniqueVar >>= \v -> return $ do n <- readTVar v writeTVar v (n+1) return n getUniqueInt :: IO Integer getUniqueInt = uniqueIntSTM >>= atomically -Judah

Judah Jacobson wrote:
I think you're saying that you want to write "w <- newIOWitness" at the top level, so that w can then be referenced in a pure function. Fair enough. But newIOWitness's implementation requires writeIORef (or an equivalent), which is not ACIO, right?
newIOWitness is very like newUnique. In both cases, the internal implementation updates an MVar to make them unique. Internally the open-witness package would use unsafeIOtoACIO (just as it already uses unsafeCoerce), but an exposed newIOWitnessACIO would be safe.
oneshot uniqueVar :: IO (TVar Integer) uniqueVar =< atomically $ newTVar 0 -- alternately, use newTVarIO
uniqueIntSTM :: IO (STM Integer) uniqueIntSTM = uniqueVar >>= \v -> return $ do n <- readTVar v writeTVar v (n+1) return n
getUniqueInt :: IO Integer getUniqueInt = uniqueIntSTM >>= atomically
This complicates the purpose of STM, which is to make composable STM transactions. I would rather do this: uniqueVar :: TVar Integer uniqueVar <- newTVarACIO uniqueInt :: STM Integer uniqueInt = do n <- readTVar uniqueVar writeTVar uniqueVar (n+1) return n AFAICT, one-shots are less powerful and just as complicated as an ACIO monad. -- Ashley Yakeley

On Tue, Aug 26, 2008 at 01:14:34AM -0700, Judah Jacobson wrote:
On Tue, Aug 26, 2008 at 12:07 AM, Adrian Hey
wrote: But from a top level aThing <- someACIO point of view, if we're going to say that it doesn't matter if someACIO is executed before main is entered (possibly even at compile time) or on demand, then we clearly don't want to observe any difference between the latter case and the former (if aThing becomes garbage without ever being demanded).
Maybe it would be safest to just say anything with a finaliser can't be created at the top level. We can always define an appropriate top level "get" IO action using runOnce or whatever.
I've been wondering: is there any benefit to having top-level ACIO'd <- instead of just using runOnce (or perhaps "oneshot") as the primitive for everything? For example:
oneshot uniqueRef :: IO (MVar Integer) uniqueRef =< newMVar 0
It was also suggested in that wiki page: http://haskell.org/haskellwiki/Top_level_mutable_state#Proposal_4:_Shared_on...
Those proposals eliminate the need for creating an ACIO monad and enforcing its axioms, since one-shot actions are executed in-line with other I/O actions (rather than at some nebulous "before the program is run" time).
Actually, due to the definition of ACIO, there is no difference between the two (for actions actually in ACIO). It was formulated to have this property. both implementations (executing them before the program is run, or on first call) and ways of thinking about things are valid and will be indistinguishable for all proper ACIO actions. note, you can implement oneshot IO actions on top of ACIO top level actions, but not the reverse. I think ACIO is cleaner overall, since we have a nice formal definition of when ACIO actions are valid without having to invoke the more complicated IO monad. John -- John Meacham - ⑆repetae.net⑆john⑈

Judah Jacobson wrote:
I've been wondering: is there any benefit to having top-level ACIO'd <- instead of just using runOnce (or perhaps "oneshot") as the primitive for everything? For example:
oneshot uniqueRef :: IO (MVar Integer) uniqueRef =< newMVar 0
I've been wondering about something like this too (in some way just have a oneShot or runOnce and the *only* thing in ACIO as a magic primitive). runOnce :: IO a -> ACIO (IO a) It would certainly simplify the ACIO monad :-), but I'm not sure it's really as flexible. Provided newMVar can be ACIO then this can be implemented directly (doesn't need to be a primitive). But we can't go the other way round (use runOnce to create genuine top level MVars or channels say). Does that matter? Probably not for monadic IO code. It's not a huge inconvenience to write.. do ... thing <- getThing foo thing vs.. do ... foo thing -- thing is at top level But for top level non monadic code/expressions/data structures I can see a certain convenience in having thing as top level identifier if possible, which it often won't be anyway I guess for other reasons (like it's creation and initialisation requires real IO). So I don't have any particularly strong opinion either way. In practice if thing (or getThing) is to be exported then it would probably be prudent to assume creation and initialisation might require real IO at some point in the future even if they don't right now, so you'd export getThing (= return thing) anyway, rather then have an exported thing dissappear from the API at some point. My 2p.. Regards -- Adrian Hey

I think a strong advantage of the straight up ACIO formulation (rather than a one-shot IO based thing) is that it can be fully, correctly, and safely be defined without referencing the IO monad or its interaction with the IO monad at all. In practice, ACIO will be generally be used to interact with IO code. But being able to formalize it completely independently of the IO monad and all the IO monad entails is an enticing posibility. John -- John Meacham - ⑆repetae.net⑆john⑈

Adrian Hey wrote:
Maybe it would be safest to just say anything with a finaliser can't be created at the top level.
Do you have an example of something that is correctly ACIO to create, but has a problematic finaliser? -- Ashley Yakeley

Ashley Yakeley wrote:
Adrian Hey wrote:
Maybe it would be safest to just say anything with a finaliser can't be created at the top level.
Do you have an example of something that is correctly ACIO to create, but has a problematic finaliser?
Sorry for the delay in getting my attention. I've been looking at my old ACIO code (which I've largely forgotten the details of) and dug up the following snippet. -- * Weak pointers -- | All these functions require that finalisers (if any) are in the -- ACIO Monad. -- I'm not sure what such a finaliser can usefully do, but it can't be -- in the IO Monad and preserve ACIO Monad properties AFAICS. mkWeak,mkWeakPtr,mkWeakPair,addFinalizer,mkWeakIORef, I must admit I can't remember much about any of this, I just worked my way through the IO libs trying to figure out which actions might plausibly (if not usefully :-) be regarded as ACIO. There seemed to be quite a lot at the end of the day. I also added a few forkIO variants which force the forked thread to block on certain things (empty MVar,Chan, QSem..) waiting for some IO monad action to kick them into life. Regards -- Adrian Hey

On Tue, Aug 26, 2008 at 08:07:24AM +0100, Adrian Hey wrote:
But from a top level aThing <- someACIO point of view, if we're going to say that it doesn't matter if someACIO is executed before main is entered (possibly even at compile time) or on demand, then we clearly don't want to observe any difference between the latter case and the former (if aThing becomes garbage without ever being demanded).
Maybe it would be safest to just say anything with a finaliser can't be created at the top level. We can always define an appropriate top level "get" IO action using runOnce or whatever.
If the finalizer is also in the weaker form of ACIO (ACIO under the no more references exist to its argument presumption, maybe called 'linearity condition' or something?), then it shouldn't matter at all. I can't think of any finalizers that don't obey this property that wern't problematic under the old model to begin with. John -- John Meacham - ⑆repetae.net⑆john⑈
participants (4)
-
Adrian Hey
-
Ashley Yakeley
-
John Meacham
-
Judah Jacobson