Re: [Haskell] threading mutable state through callbacks
Well, Wadler's Law of Language Design has been disproved. We seem to agree on the syntax, that is, global "foo <- bar" bindings, and we're actually discussing semantics. That's great! Just for the record, I want exactly what Adrian proposed. There seem to be two interesting points about the semantics: 1) To prevent unwanted typecasts, all toplevel "<-" bindings must be monomorphic. This restriction should be sufficient to keep everything perfectly safe (does anyone disagree?), except for... 2) Evaluation order. That's what everyone has been fighting about. So what are our options? a) unsafePerformIO - like. This has some potential for abuse, but it works. Lazy initialization is a good thing for large programs - and that includes side effects. People deliberately do that in OO programs - you'll find methods that look like getSomeMemberVar accessor methods, but the first time they're invoked, they'll actually initialize a part of the program. I'd actually like to abuse this feature that way. Anyway, the non-determinism this introduces is not nearly as bad as what you get from concurrency or even signal handlers... b) Some predetermined order, with semantics like mdo: John Meacham wrote:
The basic idea is that your entire program behaves as if in a giant 'mdo' block, ordered in module dependency order.
What is module dependency order? There's no such thing when there are circular dependencies, and when there aren't any, it might still depend on the order of import directives, which is probably not something I want program semantics to depend on. I feel that In a large program, you'll probably get more surprises from this. The good thing is that you can always use unsafeInterleaveIO to get the other semantics back, so we should probably investigate this further. Is there some option c) that I overlooked? Another question is, if the order is to follow program order, what do we still need main for? Ugh... Cheers, Wolfgang
On Wednesday 13 Oct 2004 3:36 am, Wolfgang Thaller wrote:
b) Some predetermined order, with semantics like mdo:
John Meacham wrote:
The basic idea is that your entire program behaves as if in a giant 'mdo' block, ordered in module dependency order.
What is module dependency order? There's no such thing when there are circular dependencies, and when there aren't any, it might still depend on the order of import directives, which is probably not something I want program semantics to depend on. I feel that In a large program, you'll probably get more surprises from this. The good thing is that you can always use unsafeInterleaveIO to get the other semantics back, so we should probably investigate this further.
Yes, this is something I wasn't too clear about, largely because I'm not too certain about the precise semantics of mdo (never actually used it). John's proposal seemed like a good formalisation of what a compiler might do in reality to implement this. But if it means imposing some order on things (other than the dependency "on demand" ordering that we currently get with the unsafePerformIO hack) I'm not sure that's a good idea. I would rather have the order unspecified and make it the programmers responsibility to control this, if significant. Having the correctness of programs dependent on ordering of top level definitions or imports is not a discipline Haskellers are used to. If this is the case with John's proposal, it seems like it could cause trouble in the long run :-( Regards -- Adrian Hey
On Wed, Oct 13, 2004 at 10:01:08AM +0100, Adrian Hey wrote:
On Wednesday 13 Oct 2004 3:36 am, Wolfgang Thaller wrote:
b) Some predetermined order, with semantics like mdo:
John Meacham wrote:
The basic idea is that your entire program behaves as if in a giant 'mdo' block, ordered in module dependency order.
What is module dependency order? There's no such thing when there are circular dependencies, and when there aren't any, it might still depend on the order of import directives, which is probably not something I want program semantics to depend on. I feel that In a large program, you'll probably get more surprises from this. The good thing is that you can always use unsafeInterleaveIO to get the other semantics back, so we should probably investigate this further.
Yes, this is something I wasn't too clear about, largely because I'm not too certain about the precise semantics of mdo (never actually used it).
John's proposal seemed like a good formalisation of what a compiler might do in reality to implement this. But if it means imposing some order on things (other than the dependency "on demand" ordering that we currently get with the unsafePerformIO hack) I'm not sure that's a good idea.
I would rather have the order unspecified and make it the programmers responsibility to control this, if significant. Having the correctness of programs dependent on ordering of top level definitions or imports is not a discipline Haskellers are used to. If this is the case with John's proposal, it seems like it could cause trouble in the long run :-(
Yeah, there actually is not a real need to formalize the order, since the top-level bindings are going to be recursive and lazy, A specific order doesn't matter much more than what order you allocate your CAFs in. However, since IO actions can have side effects and enforce evaluation, one may specifically write code which will be able to observe the order. This doesn't seem like that big of a deal, since the result of an IO action is allowed in some sense to depend on implementation or undefined behavior and it certainly shouldn't be a problem for the common cases of IORefs, MVars and global Channels. I would be perfectly happy with just specifying an unknown order, or just that all the bindings in a single module be ordered in the same way as they appear in the module, but intramodule orders be unspecified. the method I posted happens to execute the actions in dependency order, but there is no need to guarentee that. John -- John Meacham - ⑆repetae.net⑆john⑈
Wolfgang Thaller wrote:
2) Evaluation order.
That's what everyone has been fighting about. [...]
The basic idea is that your entire program behaves as if in a giant 'mdo' block, ordered in module dependency order.
What is module dependency order? There's no such thing when there are circular dependencies, and when there aren't any, it might still depend on the order of import directives, which is probably not something I want program semantics to depend on. I feel that In a large program, you'll probably get more surprises from this.
The Modula-3 language specification gives one reasonable definition: ===Begin excerpt. From http://research.compaq.com/SRC/m3defn/html/complete.html#idx.148 : 2.5.6 Initialization The order of execution of the modules in a program is constrained by the following rule: If module M depends on module N and N does not depend on M, then N's body will be executed before M's body, where: A module M depends on a module N if M uses an interface that N exports or if M depends on a module that depends on N. A module M uses an interface I if M imports or exports I or if M uses an interface that (directly or indirectly) imports I. Except for this constraint, the order of execution is implementation-dependent. ===End excerpt. According to the above constraint, the initialization order is implementation-defined in the case of circular dependencies. Note, too, that the order of import directives in a module does not affect the partial order imposed by the above constraint. (The above was delayed by the haskell mailing list anti-spam measures. Anyway, looks like the above rule is entirely consistent with Simon Marlow's description of what ghc already implements for initialization order.) -Antony
participants (4)
-
Adrian Hey -
Antony Courtney -
John Meacham -
Wolfgang Thaller