RE: Top level mutable data structures problem

Lots of schemes have been discussed. The one I understand best is: * Modules can contain top-level bindings like x <- e where e:: IO t, and x::t * The IO actions from these bindings are composed, in order of appearance, and together comprise the "module initialisation action" * When a program is started, the module initialisation actions of its modules run, in an order that respects module dependencies. * "Respecting module dependencies" means that if M imports N (directly or indirectly) then N's initialisation is done before M's. Hi-boot recursive dependencies are not taken into account; that's where any module loops are broken That's simple, and easy to describe. It does not fully prescribe the initialisation order, but the programmer can constrain the order as much as required, by adding 'import' declarations. That's way better than the situation today, where the moment at which these actions take place is totally unpredictable. As Simon M says, don't hold your breath... but I'd be interested to know a) whether this story commands a consensus b) how much happier your life would be if it were implemented Simon | -----Original Message----- | From: glasgow-haskell-users-bounces@haskell.org [mailto:glasgow-haskell-users- | bounces@haskell.org] On Behalf Of Simon Marlow | Sent: 20 October 2004 15:47 | To: Adrian Hey; glasgow-haskell-users@haskell.org | Subject: RE: Top level mutable data structures problem | | On 20 October 2004 14:36, Adrian Hey wrote: | | > [Excuse me for moving this discussion to the ghc mailing list, | > but it seems the appropriate place, seeing as ghc is where | > any solution will happen first in all probability.] | > | > I've noticed that the neither of the two Simons has expressed an | > opinion re. the discussions on this issue that occurred on the | > Haskell mailing list over the weekend. So I'd like to ask what's | > happening (or likely to happen) about this, if anything? | | I liked the original idea. I'm not sure if I agree with the argument | that allowing fully-fledged IO actions in the initialisation of a module | is unsafe. I agree that it is a little opaque, in the sense that one | can't easily tell whether a particular init action is going to run or | not. | | On the other hand, instances currently have the same "problem": you | can't tell what instances are in scope in your module without looking | through the transitive closure of modules you import, including | libraries, which you might not have source code for. | | The proposed scheme wouldn't allow forcing an ordering on init actions | between two modules (eg. initialise the network library, then initialise | the HTTP library). | | In any case, we're not going to rush to implement anything. Discuss it | some more ;-) | | Cheers, | Simon | _______________________________________________ | Glasgow-haskell-users mailing list | Glasgow-haskell-users@haskell.org | http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

On Wed, Oct 20, 2004 at 04:38:54PM +0100, Simon Peyton-Jones wrote:
b) how much happier your life would be if it were implemented
Or... How much sadder your life will be if this mechanism will be abused and overused. Someone already noticed that with such and extension main is no longer neccessary. Imagine you have to check all modules (even those that are only imported, but not used) to understand what the program is doing. In current situation I can quickly eliminate some modules from consideration. I didn't follow the whole discussion, but there was some proposition, by John Hughes IIRC, to extend the implicit parameter mechanism by allowing to leave some part of type signature unspecified. This would help in situations where you want to write the type signatures of functions, but you would rather like to omit the types of implicit parameters. If it's not clear, I am talking about using implicit parameters to emulate global variables. This has the benefit that the use of global variables is controlled, checked by the compiler. Best regards, Tom -- .signature: Too many levels of symbolic links

On Wed, Oct 20, 2004 at 05:54:37PM +0200, Tomasz Zielonka wrote:
On Wed, Oct 20, 2004 at 04:38:54PM +0100, Simon Peyton-Jones wrote:
b) how much happier your life would be if it were implemented
Or... How much sadder your life will be if this mechanism will be abused and overused. Someone already noticed that with such and extension main is no longer neccessary. Imagine you have to check all modules (even those that are only imported, but not used) to understand what the program is doing. In current situation I can quickly eliminate some modules from consideration. [snip]
I think that if those bindings would be lazy the way they are now (using unsafePerformIO) both risks would be lower: - `Import' continues not to have weird side-effects. - Complex order-dependant initialisations (more complex than newIORef's "must be initialized before use") cannot so easily be done. - when one really wants to do it that way he can still use something like this: main | v1 `seq` v2 `seq` False = undefined | otherwise = do ... Which doesn't mean I'm in favour of that either. Greetings, Remi "waiting to be convinced" Turk -- Nobody can be exactly like me. Even I have trouble doing it.

On Wednesday 20 Oct 2004 4:38 pm, Simon Peyton-Jones wrote:
* "Respecting module dependencies" means that if M imports N (directly or indirectly) then N's initialisation is done before M's. Hi-boot recursive dependencies are not taken into account; that's where any module loops are broken
I was hoping that one day Hi-boot would not be neccessary. So I'm not sure what ordering you'd get in that case with what's proposed. But let's not worry about that right now :-)
As Simon M says, don't hold your breath... but I'd be interested to know
a) whether this story commands a consensus
Well actually at the moment I don't agree with this proposal, but I may be in a minority of one. That's why I wanted to find out what's happening. (See my response to Simon M for my reasons). Wolfgang definitely seems to want this though, but I'd better leave it to him to explain why. IMO the one good thing about the unsafePerformIO hack is that there are no guarantees about whether or when the corresponding actions will occur. This fact is a powerful disinsentive to abuse of or over reliance on this feature I think. I.E. People are not likely to use it for any purpose other than the creation of top level mutable data structures. In particular they can't rely on any "side effects" of creation if there's no guarantee that creation will ever occur. (Well not unless they use `seq` somewhere to force it).
b) how much happier your life would be if it were implemented
Well IME there are two common uses of unsafePerformIO in my progs.. 1- Creation of top level mutable data structures (which may live outside Haskell land sometimes). 2- As a "type cast" for FFI functions which really are pure, but have type IO anyway because of marshalling etc.. I would really like a solution to useage 1 that didn't use unsafePerformIO, because it really is _unsafe_. Useage 2 is ugly perhaps, but AFAICS is perfectly safe (provided the foreign function in question really is a function), so doesn't require the use of NOINLINE, -fno-cse etc. Regards -- Adrian Hey

On Wed, Oct 20, 2004 at 04:38:54PM +0100, Simon Peyton-Jones wrote:
* When a program is started, the module initialisation actions of its modules run, in an order that respects module dependencies.
What happens when there are cicular dependencies between modules. Perhaps the circular dependency is only because of sharing of types, and there is a valid order for the initialization. But, is it easy for the compiler to determine this? Dave

On Thu, Oct 21, 2004 at 12:17:52PM -0700, David Brown wrote:
On Wed, Oct 20, 2004 at 04:38:54PM +0100, Simon Peyton-Jones wrote:
* When a program is started, the module initialisation actions of its modules run, in an order that respects module dependencies.
What happens when there are cicular dependencies between modules. Perhaps the circular dependency is only because of sharing of types, and there is a valid order for the initialization. But, is it easy for the compiler to determine this?
I don't see a problem with letting the compiler run them in an arbitrary order. I don't see much need for ordering things intermodule as long as the actions are executed in order within each module. John -- John Meacham - ⑆repetae.net⑆john⑈

On Fri, Oct 22, 2004 at 05:13:02PM -0700, John Meacham wrote:
What happens when there are cicular dependencies between modules. Perhaps the circular dependency is only because of sharing of types, and there is a valid order for the initialization. But, is it easy for the compiler to determine this?
I don't see a problem with letting the compiler run them in an arbitrary order. I don't see much need for ordering things intermodule as long as the actions are executed in order within each module.
Although many programs will not depend on the ordering, others can. What if one module initialized a file, and another was expecting it to be setup correctly. Or, perhaps there is a shared IORef that needs to be initialized. Once IO is in the picture, the dependency computation can become complex. In general, if module A references module B, then module B's initialization should have finished before module B's. As long as there are no circular references, it isn't difficult to come up with a valid ordering (aside from references to external entities). When A and B each refer to the other, the compiler can't automatically determine which initialization must be done first. Dave
participants (6)
-
Adrian Hey
-
David Brown
-
John Meacham
-
Remi Turk
-
Simon Peyton-Jones
-
Tomasz Zielonka