Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008-08-28 14:45 -0700 (Thu), Jonathan Cast wrote: Now, I happen to know that the only top-level handles that can be established without issuing an open system call are stdin stdout stderr (unless you're happy to have your global nonStdErr start its life attached to an unopened FD). I've not thought through exactly how this might relate to your argument, but certainly, though there might or might not be Haskell Handles for other file descriptors, they can start out open without calling open. Compile this simple program: #import stdio.h int main() { int n; n = write(5, foobar\n, 7); printf(write returned %d\n, n); return 0; } and run it with ./a.out 51 and have a look at the result you get. cjs -- Curt Sampson [EMAIL PROTECTED]+81 90 7737 2974 Mobile sites and software consulting: http://www.starling-software.com ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] RE: [Haskell] Top Level -
Ashley Yakeley wrote: Sittampalam, Ganesh wrote: Oh dear. To fix this, I suppose the RTS would have to be able to keep track of all static initialisers. But it shouldn't otherwise affect program optimisation. What would the RTS actually do? I don't know enough about the RTS to say. I imagine initialisers would have to be marked in object files, so the RTS could link them separately when dynamically loading. The RTS would also keep a list of initialisers in the main program. Sounds plausible, although dynamic relocations do slow down linking. Unloading is another interesting problem. Are we allowed to re-run - if the module that contained it is unloaded and then reloaded? I'm not quite sure what the conditions for allowing a module to be unloaded in general should be, though. Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Sittampalam, Ganesh wrote: Sounds plausible, although dynamic relocations do slow down linking. Unloading is another interesting problem. Are we allowed to re-run - if the module that contained it is unloaded and then reloaded? I'm not quite sure what the conditions for allowing a module to be unloaded in general should be, though. Interesting question. I suppose it's allowable if the guarantees attached to the ACIO type imply that it would not be possible to tell the difference. I think this means that all values of types, including newtypes, belonging to the module must be unreachable before unloading. Consider Data.Unique as a separate loadable module. It's loaded, and various Unique values are obtained. But Unique is just a newtype of Integer, and comparison between Uniques doesn't use code from Data.Unique. This might be difficult to track as once the newtype is boiled away, the code is basically dealing with Integers, not Uniques. I really don't know enough about the RTS to know. The alternative would be to keep all initialised values when the module is unloaded. I'm guessing this is more feasible. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] RE: [Haskell] Top Level -
Ashley Yakeley wrote: I really don't know enough about the RTS to know. The alternative would be to keep all initialised values when the module is unloaded. I'm guessing this is more feasible. Easier, but a guaranteed memory leak. Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Sittampalam, Ganesh wrote: Ashley Yakeley wrote: I really don't know enough about the RTS to know. The alternative would be to keep all initialised values when the module is unloaded. I'm guessing this is more feasible. Easier, but a guaranteed memory leak. But it's limited to the initialisers. An IORef holding an Integer isn't much memory, and it only ever gets leaked once. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] RE: [Haskell] Top Level -
Ashley Yakeley wrote: Sittampalam, Ganesh wrote: In compiled code module boundaries don't necessarily exist. So how do you relink the loaded code so that it points to the unique copy of the module? hs-plugins loads modules as single .o files, I believe. Yes, but (a) the loading program doesn't and (b) that's an implementation choice, not a necessity. Two RTSs? Are you quite sure? How would GC work? I talked to Don about this and you're right, that doesn't happen. However he also confirmed that it does load modules a second time if they are in the main program as well as the plugin, and it would be difficult to merge the static and dynamic versions of the module. Cheers, Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Sittampalam, Ganesh wrote: I talked to Don about this and you're right, that doesn't happen. However he also confirmed that it does load modules a second time if they are in the main program as well as the plugin, and it would be difficult to merge the static and dynamic versions of the module. Oh dear. To fix this, I suppose the RTS would have to be able to keep track of all static initialisers. But it shouldn't otherwise affect program optimisation. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
The IO sin bin [was: Re: [Haskell-cafe] Re: [Haskell] Top Level -]
Adrian Hey wrote: There's shed loads of information and semantic subtleties about pretty much any operation you care to think of in the IO monad that isn't communicated by it's type. All you know for sure is that it's weird, because if it wasn't it wouldn't be in the IO monad. So I think you're applying double standards. Not to throw any more fuel on the fire (if at all possible), but the reason behind this is that IO has become a sin bin for all the things that people either don't know how to deal with, or don't care enough to tease apart. There are many people who would like to break IO apart into separate segments for all the different fragments of the RealWorld that actually matter for a given purpose. To date it has not been entirely clear how best to do this and retain a workable language. The fact that this discussion is going on at all is, IMO, precisely because of the sin-bin nature of IO. People have things they want to have global or otherwise arbitrarily large scope, but the only notion of a globe in Haskell is the RealWorld. Hence they throw things into IO and then unsafePerformIO it to get it back out. There are three problems to this approach: (1) It's a hack and not guaranteed to work, nuff said. (2) The RealWorld is insufficiently described to ensure any semantics regarding *how* it holds onto the state requested of it. This problem manifests itself in the discussion of loading the same library multiple times, having multiple RTSes in a single OS process, etc. In those scenarios what exactly the RealWorld is and how the baton is passed among the different libraries/threads/processes/RTSes is not clearly specified. (3) The API language is insufficiently detailed to make demands on what the RealWorld holds. This problem manifests itself in the argument about whether libraries should be allowed to implicitly modify portions of the RealWorld, or whether this requirement should be made clear in the type signatures of the library. As I said in the thread on [Research language vs. professional language], I think coming up with a solution to this issue is still an open research problem, and one I think Haskell should be exploring. The ACIO monad has a number of nice properties and I think it should be broken out from IO even if top-level - aren't added to the language. The ability to declare certain FFI calls as ACIO rather than IO is, I think, reason enough to pursue ACIO on its own. But, ACIO does not solve the dilemmas raised in #2 and #3. Top-level mutable state is only a symptom of the real problem that IO and the RealWorld are insufficiently described. Another example where unsafePerformIO is used often is when doing RTS introspection. Frequently, interrogating the RTS has ACIO-like properties in that we are only interested in the RTS if a particular thunk happens to get pulled on, and we're only interested at the time that the pulling occurs rather than in sequence to any other actions. The use of unsafePerformIO here seems fraught with all the same problems as top-level mutable state. It would be nice to break out an RTS monad (or an UnsafeGhcRTS monad, or what have you) in order to be more clear about the exact requirements of what's going on. But even if we break ACIO and UnsafeGhcRTS out from IO, the dilemmas remain. To a certain extent, the dilemmas will always remain because there will always be a frontier beyond which we don't know what's happening: the real world exists, afterall. However, there is still room to hope for a general approach to the problem. One potential is to follow on the coattails of _Data Types a la Carte_. Consider, for example, if the language provided a mechanism for users to generate RealWorld-like non-existent tokens. Now consider removing IO[1] and only using BS, where the thread-state parameter could be any (co)product of RealWorld-like tokens. We could then have an overloaded function to lift any (BS a) into a BS (a :+: b). There are some complications with DTalC's coproducts in practice. For example, (a :+: b) and (b :+: a) aren't considered the same type, as naturally they can't be due to the Inl/Inr tagging. A similar approach should be able to work however, since these tokens don't really exist at all. Of course, once we take things to that level we're already skirting around capability systems. Rather than using an ad-hoc approach like this, I think it would be better to work out a theory connecting capability systems to monad combinators, and then use that theory to smash the sin bin of IO. [1] Or leaving it in as a type alias for BS RealWorld. -- Live well, ~wren ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Sittampalam, Ganesh wrote: Oh dear. To fix this, I suppose the RTS would have to be able to keep track of all static initialisers. But it shouldn't otherwise affect program optimisation. What would the RTS actually do? I don't know enough about the RTS to say. I imagine initialisers would have to be marked in object files, so the RTS could link them separately when dynamically loading. The RTS would also keep a list of initialisers in the main program. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: In any case, what I'm trying to establish below is that it should be a safety property of - that the entire module (or perhaps mutually recursive groups of them?) can be duplicated safely - with a new name, or as if with a new name - and references to it randomly rewritten to the duplicate, as long as the result still type checks. That's not acceptable. This would cause Unique to break, as its MVar would be created twice. It would also mean that individual Unique and IOWitness values created by - would have different values depending on which bit of code was referencing them. It would render the extension useless as far as I can see. It also introduces odd execution scopes again. In order for - to work, it must be understood that a given - initialiser in a given module in a given version of a given package will execute at most once per RTS. But your restriction breaks that. It's worth mentioning that the current Data.Unique is part of the standard base library, while hs-plugins is rather experimental. Currently Data.Unique uses the NOINLINE unsafePerformIO hack to create its MVar. If hs-plugins duplicates that MVar, that's a bug in hs-plugins. It's up to a dynamic loader to get initialisation code correct. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] Re: [Haskell] Top Level -
Ashley Yakeley wrote: Ganesh Sittampalam wrote: In any case, what I'm trying to establish below is that it should be a safety property of - that the entire module (or perhaps mutually recursive groups of them?) can be duplicated safely - with a new name, or as if with a new name - and references to it randomly rewritten to the duplicate, as long as the result still type checks. That's not acceptable. This would cause Unique to break, as its MVar would be created twice. It would also mean that individual Unique and IOWitness values created by - would have different values depending on which bit of code was referencing them. It would render the extension useless as far as I can see. The result wouldn't typecheck if two Unique values that now pointed to the two different modules were compared. Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
David Menendez wrote: Isn't that what we have right now? Typeable gives you a TypeRep, which can be compared for equality. All the introspection stuff is in Data. Oh, yes, you're right. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Wed, Sep 3, 2008 at 2:53 AM, Ashley Yakeley [EMAIL PROTECTED] wrote: It's worth mentioning that the current Data.Unique is part of the standard base library, while hs-plugins is rather experimental. Currently Data.Unique uses the NOINLINE unsafePerformIO hack to create its MVar. If hs-plugins duplicates that MVar, that's a bug in hs-plugins. It's up to a dynamic loader to get initialisation code correct. Data.Unique describes itself as experimental and non-portable. The Haskell 98 report includes NOINLINE, but also states that environments are not required to respect it. So hs-plugins wouldn't necessarily be at fault if it didn't support Data.Unique. -- Dave Menendez [EMAIL PROTECTED] http://www.eyrie.org/~zednenem/ ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] Re: [Haskell] Top Level -
Dave Menendez wrote: The Haskell 98 report includes NOINLINE, but also states that environments are not required to respect it. So hs-plugins wouldn't necessarily be at fault if it didn't support Data.Unique. Also, the definition of NOINLINE in the report doesn't preclude copying both the MVar *and* its use sites, which is what I am proposing should be considered generally safe. Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Sittampalam, Ganesh wrote: That's not acceptable. This would cause Unique to break, as its MVar would be created twice. It would also mean that individual Unique and IOWitness values created by - would have different values depending on which bit of code was referencing them. It would render the extension useless as far as I can see. The result wouldn't typecheck if two Unique values that now pointed to the two different modules were compared. I don't understand. If the dynamic loader were to load the same package name and version, and it duplicated the MVar, then Unique values would have the same type and could be compared. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
David Menendez wrote: On Wed, Sep 3, 2008 at 2:53 AM, Ashley Yakeley [EMAIL PROTECTED] wrote: It's worth mentioning that the current Data.Unique is part of the standard base library, while hs-plugins is rather experimental. Currently Data.Unique uses the NOINLINE unsafePerformIO hack to create its MVar. If hs-plugins duplicates that MVar, that's a bug in hs-plugins. It's up to a dynamic loader to get initialisation code correct. Data.Unique describes itself as experimental and non-portable. The Haskell 98 report includes NOINLINE, but also states that environments are not required to respect it. So hs-plugins wouldn't necessarily be at fault if it didn't support Data.Unique. I found this: To solve this the hs-plugins dynamic loader maintains state storing a list of what modules and packages have been loaded already. If load is called on a module that is already loaded, or dependencies are attempted to load, that have already been loaded, the dynamic loader ignores these extra dependencies. This makes it quite easy to write an application that will allows an arbitrary number of plugins to be loaded. http://www.cse.unsw.edu.au/~dons/hs-plugins/hs-plugins-Z-H-6.html -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] RE: [Haskell] Top Level -
Ashley Yakeley wrote: I don't understand. If the dynamic loader were to load the same package name and version, and it duplicated the MVar, then Unique values would have the same type and could be compared. I am suggesting that this duplication process, whether conducted by the dynamic loader or something else, should behave as if they did not have the same package name or version. This is certainly a valid transformation for Data.Unique, I am simply saying that it should be a valid transformation on any module. Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] Re: [Haskell] Top Level -
Ashley Yakeley wrote: To solve this the hs-plugins dynamic loader maintains state storing a list of what modules and packages have been loaded already. If load is called on a module that is already loaded, or dependencies are attempted to load, that have already been loaded, the dynamic loader ignores these extra dependencies. This makes it quite easy to write an application that will allows an arbitrary number of plugins to be loaded. http://www.cse.unsw.edu.au/~dons/hs-plugins/hs-plugins-Z-H-6.html My recollection from using it a while ago is that if a module is used in the main program it will still be loaded once more in the plugin loader. This is because the plugin loader is basically an embedded copy of ghci without much knowledge of the host program's RTS. Cheers, Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Sittampalam, Ganesh wrote: I am suggesting that this duplication process, whether conducted by the dynamic loader or something else, should behave as if they did not have the same package name or version. This is certainly a valid transformation for Data.Unique, I am simply saying that it should be a valid transformation on any module. So if I dynamically load module M that uses base, I will in fact get a completely new and incompatible version of Maybe, IO, [], Bool, Char etc. in all the type-signatures of M? -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] RE: [Haskell] Top Level -
Ashley Yakeley wrote: Sittampalam, Ganesh wrote: I am suggesting that this duplication process, whether conducted by the dynamic loader or something else, should behave as if they did not have the same package name or version. This is certainly a valid transformation for Data.Unique, I am simply saying that it should be a valid transformation on any module. So if I dynamically load module M that uses base, I will in fact get a completely new and incompatible version of Maybe, IO, [], Bool, Char etc. in all the type-signatures of M? I think it treats them as compatible, using the fact that Data.Typeable returns the same type reps (which was why I initially mentioned Data.Typeable in this thread). This is fine for normal modules. There's a bit of description in the Dynamic Typing section of http://www.cse.unsw.edu.au/~dons/hs-plugins/hs-plugins-Z-H-5.html#node_s ec_9 It's clearly the wrong thing to do for Data.Unique and any anything else that might use -; but if there are no such types in the interface of the plugin, then it won't matter. I can't see how to make it safe to pass Data.Unique etc across a plugin interface without severely restricting the possible implementation strategies for a plugin library and its host. Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Sittampalam, Ganesh wrote: I think it treats them as compatible, using the fact that Data.Typeable returns the same type reps (which was why I initially mentioned Data.Typeable in this thread). This is fine for normal modules. There's a bit of description in the Dynamic Typing section of http://www.cse.unsw.edu.au/~dons/hs-plugins/hs-plugins-Z-H-5.html#node_s ec_9 It's clearly the wrong thing to do for Data.Unique and any anything else that might use -; but if there are no such types in the interface of the plugin, then it won't matter. I can't see how to make it safe to pass Data.Unique etc across a plugin interface without severely restricting the possible implementation strategies for a plugin library and its host. I think it's bad design for a dynamic loader to load a module more than once anyway. It's a waste of memory, for a start. We already know that hs-plugins won't for modules it already loaded itself (apparently it crashes the RTS), and I suspect it doesn't at all. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] RE: [Haskell] Top Level -
Ashley Yakeley wrote: I think it's bad design for a dynamic loader to load a module more than once anyway. In compiled code module boundaries don't necessarily exist. So how do you relink the loaded code so that it points to the unique copy of the module? It's a waste of memory, for a start. We already know that hs-plugins won't for modules it already loaded itself (apparently it crashes the RTS), and I suspect it doesn't at all. It crashes the RTS of the plugins loader, which is based on ghci, which is built around loading modules independently. I believe there's a separate RTS running at the top level of the program which has no knowledge of the plugin loader. Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Ashley Yakeley wrote: Currently Data.Unique uses the NOINLINE unsafePerformIO hack to create its MVar. If hs-plugins duplicates that MVar, that's a bug in hs-plugins. Sittampalam, Ganesh wrote: Also, the definition of NOINLINE in the report doesn't preclude copying both the MVar *and* its use sites, Right. It would not be a bug in hs-plugins. That is the most urgent problem right now. It is nice to discuss various proposed new language features. That is the way to solve the problem in the long term. But right now - there is no way to do this in Haskell at all. The NOINLINE unsafePerformIO hack doesn't really work. This is currently a major hole in Haskell in my opinion. For the short term - can we *please* get an ONLYONCE pragma that has the correct semantics? Thanks, Yitz ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] Re: [Haskell] Top Level -
Yitzhak Gale wrote: Right. It would not be a bug in hs-plugins. That is the most urgent problem right now. [...] For the short term - can we *please* get an ONLYONCE pragma that has the correct semantics? So the purpose of this pragma would solely be so that you can declare hs-plugins buggy for not respecting it? Or do you have some way to fix hs-plugins so that it does do so? (Assuming that my belief about how hs-plugins works is correct, of course) Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
I wrote: For the short term - can we *please* get an ONLYONCE pragma that has the correct semantics? Sittampalam, Ganesh wrote: So the purpose of this pragma would solely be so that you can declare hs-plugins buggy for not respecting it? No, the hs-plugins problem - whether hypothetical or real - is only a symptom. There is no way to define global variables in Haskell right now. The NOINLINE hack is used, and most often works. But really it's broken and there are no guarantees, because NOINLINE does not have the right semantics. This is demonstrated by your hs-plugins example, but it's a general problem. Until a permanent solution is implemented and deployed in the compilers (if ever), can we please have a pragma that allows the current hack to really work? Thanks, Yitz ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] Re: [Haskell] Top Level -
(apologies for misspelling your name when quoting you last time) Yitzchak Gale wrote: For the short term - can we *please* get an ONLYONCE pragma that has the correct semantics? Until a permanent solution is implemented and deployed in the compilers (if ever), can we please have a pragma that allows the current hack to really work? How do you propose that this pragma would be implemented? Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
For the short term - can we *please* get an ONLYONCE pragma that has the correct semantics? Sittampalam, Ganesh wrote: How do you propose that this pragma would be implemented? As far as I know now, in GHC it could currently just be an alias for NOINLINE, but the GHC gurus could say for sure. Except it should require a monomorphic constant - otherwise the guarantee doesn't make sense. And it would have clear comments and documentation that state that it guarantees that the value will be computed at most once. That way, bugs could be filed against it if that ever turns out not to be true. Other applications and libraries that support the pragma - such as other compilers, and hs-plugins - would be required to respect the guarantee, and bugs could be filed against them if they don't. Thanks, Yitz ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
RE: [Haskell-cafe] Re: [Haskell] Top Level -
Yitzchak Gale wrote Other applications and libraries that support the pragma - such as other compilers, and hs-plugins - would be required to respect the guarantee, and bugs could be filed against them if they don't. If hs-plugins were loading object code, how would it even know of the existence of the pragma? Given such knowledge, how would it implement it? Ganesh == Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html == ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
I wrote Other applications and libraries that support the pragma - such as other compilers, and hs-plugins - would be required to respect the guarantee, and bugs could be filed against them if they don't. Sittampalam, Ganesh wrote: If hs-plugins were loading object code, how would it even know of the existence of the pragma? Given such knowledge, how would it implement it? Good point. A compiler pragma is only that, in the end. This is just a hack, we can only do the best we can with it. Regards, Yitz ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Wed, Sep 3, 2008 at 9:30 AM, Yitzchak Gale [EMAIL PROTECTED] wrote: I wrote Other applications and libraries that support the pragma - such as other compilers, and hs-plugins - would be required to respect the guarantee, and bugs could be filed against them if they don't. Sittampalam, Ganesh wrote: If hs-plugins were loading object code, how would it even know of the existence of the pragma? Given such knowledge, how would it implement it? Good point. A compiler pragma is only that, in the end. This is just a hack, we can only do the best we can with it. How does the FFI handle initialization? Presumably, we can link to libraries that have internal state. Could someone, in principle, use the FFI to create a global variable? -- Dave Menendez [EMAIL PROTECTED] http://www.eyrie.org/~zednenem/ ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Sittampalam, Ganesh wrote: In compiled code module boundaries don't necessarily exist. So how do you relink the loaded code so that it points to the unique copy of the module? hs-plugins loads modules as single .o files, I believe. It crashes the RTS of the plugins loader, which is based on ghci, which is built around loading modules independently. I believe there's a separate RTS running at the top level of the program which has no knowledge of the plugin loader. Two RTSs? Are you quite sure? How would GC work? The loader is a binding to the GHC runtime system's dynamic linker, which does single object loading. GHC also performs the necessary linking of new objects into the running process. http://www.cse.unsw.edu.au/~dons/hs-plugins/hs-plugins-Z-H-2.html#node_sec_4 -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: You see this as a requirement that can be discharged by adding the ACIO concept; I see it as a requirement that should be communicated in the type. Another way of looking at it is that Data.Unique has associated with it some context in which Unique values are safely comparable. You want that context to always be the top-level/RTS scope, I would like the defining that context to be part of the API. But why pick on Data.Unique as special? Because I just happened to have pointed out it uses a global variable? If you didn't know this I suspect this issue just wouldn't be an issue at all. Why haven't you raised a ticket complaining about it's API having the wrong type sigs? :-) There's shed loads of information and semantic subtleties about pretty much any operation you care to think of in the IO monad that isn't communicated by it's type. All you know for sure is that it's weird, because if it wasn't it wouldn't be in the IO monad. So I think you're applying double standards. We have to have something concrete to discuss and this is the simplest. Like I said there are a dozen or so other examples in the base package last time I counted Would you mind listing them? It might help provide some clarity to the discussion. Here's what you can't find in the libs distributed with ghc. Note this does not include all uses of unsafePerformIO. It only includes uses to make a global variable. Control.Concurrent 1 Control.OldException 1 Data.HashTable 1 Data.Typeable1 Data.Unique 1 GHC.Conc 8 GHC.Handle 3 System.Random1 Language.Haskell.Syntax 1 System.Posix.Signals 2 System.Win32.Types 1 Network.BSD 1 System.Posix.User1 Total: 23 In the ghc source you can find 16 uses of the GLOBAL_VAR macro (can't imagine what that does :-). I didn't even attempt to figure out how global variables might be the rts source. Anyone care to hazard a guess? You can find a few more in the extra libs.. Graphics.UI.GLUT.Menu1 Graphics.UI.GLUT.Callbacks.Registration 3 Graphics.Rendering.OpenGL.GLU.ErrorsInternal 1 Total: 5 A few more: wxHaskell 6 c2hs 1 GTK2HS1 SDL 0 !! However, I happen to know that SDL suffers from the initialisation issue and IIRC it needs at least 1 global to stop user using an unsafe (possibly segfault inducing) calling sequence. Anyway, that's all from me because I'm bored with this thread now. Regards -- Adrian hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: I have a feeling it might be non-trivial; the dynamically loaded bit of code will need a separate copy of the module in question, since it might be loaded into something where the module is not already present. Already the dynamic loader must load the module into the same address space and GC, i.e. the same runtime. So it should be able to make sure only one copy gets loaded. What is the status of dynamic loading in Haskell? What does hs-plugins do currently? Well, the safety of - being run twice in the Data.Unique case is based around the two different Data.Unique types not being compatible. Right. The only code that can construct Unique values is internal to Data.Unique. Let's suppose some other module uses a -, but returns things based on that - that are some standard type, rather than a type it defines itself. Is module duplication still safe? In this case, duplicate modules of different versions is as safe as different modules. In other words, this situation: mypackage-1.0 that uses - mypackage-2.0 that uses - is just as safe as this situation: mypackage-1.0 that uses - otherpackage-1.0 that uses - The multiple versions issue doesn't add any problems. Well, let me put it this way; since I don't like -, and I don't particularly mind Typeable, I wouldn't accept IOWitness as an example of something that requires - to implement correctly, because I don't see any compelling feature that you can only implement with -. Why don't you like -? Surely I've addressed all the issues you raise? Multiple package versions does not actually cause any problems. Capabilities would be really nice, but the right approach for that is to create a new execution monad. There is an obligation regarding dynamic loading, but it looks like dynamic loading might need work anyway. Since this is a matter of aesthetics, I imagine it will end with a list of pros and cons. There's some unsafety somewhere in both Typeable and IOWitnesses, and in both cases it can be completely hidden from the user - with Typeable, just don't let the user define the typeOf function at all themselves. It's worse than that. If you derive an instance of Typeable for your type, it means everyone else can peer into your constructor functions and other internals. Sure, it's not unsafe, but it sure is ugly. Sometimes you want to do witness equality tests rather than type equality tests. For instance, I might have a foo exception and a bar exception, both of which carry an Int. Rather than create new Foo and Bar types, I can just create a new witness for each. This is precisely what newtype is designed for, IMO. We don't need another mechanism to handle it. It's not what newtype is designed for. Newtype is designed to create usefully new types. Here, we're only creating different dummy types so that we can have different TypeRep values, which act as witnesses. It's the TypeReps that actually do the work. It would be much cleaner to declare the witnesses directly. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
On Tue, 2 Sep 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: I have a feeling it might be non-trivial; the dynamically loaded bit of code will need a separate copy of the module in question, since it might be loaded into something where the module is not already present. Already the dynamic loader must load the module into the same address space and GC, i.e. the same runtime. So it should be able to make sure only one copy gets loaded. I don't think it's that easy, modules aren't compiled independently of each other, and there are lots of cross-module optimisations and so on. What is the status of dynamic loading in Haskell? What does hs-plugins do currently? I don't know for sure, but I think it would load it twice. In any case, what I'm trying to establish below is that it should be a safety property of - that the entire module (or perhaps mutually recursive groups of them?) can be duplicated safely - with a new name, or as if with a new name - and references to it randomly rewritten to the duplicate, as long as the result still type checks. If that's the case, then it doesn't matter whether hs-plugins loads it twice or not. Let's suppose some other module uses a -, but returns things based on that - that are some standard type, rather than a type it defines itself. Is module duplication still safe? In this case, duplicate modules of different versions is as safe as different modules. In other words, this situation: mypackage-1.0 that uses - mypackage-2.0 that uses - is just as safe as this situation: mypackage-1.0 that uses - otherpackage-1.0 that uses - The multiple versions issue doesn't add any problems. Agreed - and I further claim that duplicating the entire module itself can't cause any problems. Well, let me put it this way; since I don't like -, and I don't particularly mind Typeable, I wouldn't accept IOWitness as an example of something that requires - to implement correctly, because I don't see any compelling feature that you can only implement with -. Why don't you like -? Surely I've addressed all the issues you raise? I'm still not happy that the current specification is good enough, although I think this thread is getting closer to something that might work. Even with a good specification for -, I would rather see the need for once-only state reflected in the type of things that have such a need. There is an obligation regarding dynamic loading, but it looks like dynamic loading might need work anyway. I think the obligation should be on -, and the obligation is the duplication rule I proposed above. Since this is a matter of aesthetics, I imagine it will end with a list of pros and cons. Agreed. There's some unsafety somewhere in both Typeable and IOWitnesses, and in both cases it can be completely hidden from the user - with Typeable, just don't let the user define the typeOf function at all themselves. It's worse than that. If you derive an instance of Typeable for your type, it means everyone else can peer into your constructor functions and other internals. Sure, it's not unsafe, but it sure is ugly. True. I would argue that this is better solved with a better typeclass hierarchy (e.g. one class to supply a witness-style representation that only supports equality, then the typereps on top of that if you want introspection too). Sometimes you want to do witness equality tests rather than type equality tests. For instance, I might have a foo exception and a bar exception, both of which carry an Int. Rather than create new Foo and Bar types, I can just create a new witness for each. This is precisely what newtype is designed for, IMO. We don't need another mechanism to handle it. It's not what newtype is designed for. Newtype is designed to create usefully new types. Here, we're only creating different dummy types so that we can have different TypeRep values, which act as witnesses. It's the TypeReps that actually do the work. newtype is frequently used to create something that you can make a separate set of typeclass instances for. This is no different. You can argue that this use of newtype is wrong, but there's no point in just providing an alternative in one specific case. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Tue, 2 Sep 2008, Adrian Hey wrote: Ganesh Sittampalam wrote: You see this as a requirement that can be discharged by adding the ACIO concept; I see it as a requirement that should be communicated in the type. Another way of looking at it is that Data.Unique has associated with it some context in which Unique values are safely comparable. You want that context to always be the top-level/RTS scope, I would like the defining that context to be part of the API. But why pick on Data.Unique as special? Because I just happened to have pointed out it uses a global variable? Only because I thought it was the running example. If you didn't know this I suspect this issue just wouldn't be an issue at all. Why haven't you raised a ticket complaining about it's API having the wrong type sigs? :-) Because I don't use it, and even if I did use it I would just live with the API it has. There's shed loads of information and semantic subtleties about pretty much any operation you care to think of in the IO monad that isn't communicated by it's type. All you know for sure is that it's weird, because if it wasn't it wouldn't be in the IO monad. It does actually claim a specification, namely that no two calls to newUnique return values that compare equal. We have to have something concrete to discuss and this is the simplest. Like I said there are a dozen or so other examples in the base package last time I counted Would you mind listing them? It might help provide some clarity to the discussion. Here's what you can't find in the libs distributed with ghc. Note this does not include all uses of unsafePerformIO. It only includes uses to make a global variable. Thanks. It'd probably be a good addition to the wiki page on this topic for these to be catalogued in terms of why they are needed, though I'm (probably) not volunteering to do it :-) Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Adrian Hey wrote: Eh? Please illustrate your point with Data.Unique. What requirements does it place on it's context? (whatever that might mean :-) It requires that its context initialises it precisely once. It's context being main? If so this is true, but I don't see why this is a problem. It's a happy accident with the unsafePerformIO hack as it is, and part of the defined semantics for *all* hypothetical top level - bindings. Though to be more precise, the requirement is that it may be initialised at any time prior to first use, but never again (there's no requirement to initialise it at all if it isn't used). Also ACIO monad properties guarantee that it's always initialised to the same value regardless of when this occurs. So I don't see the problem. Data.Unique is actually a poor example, as it is actually fine to initialise it multiple times as long as the resulting Unique values aren't treated as coming from the same datatype. I just don't see what you're getting at. There's no problem here and Data.Unique is not special. We don't even have to consider whether or not it's OK to reinitialise these things unless the programmer explicitly allows this in the API (which Data.Unique doesn't). This is true for all top level - bindings. myCount :: MVar Int myCount - newMVar 0 In a hypothetical second initialisation, do you mean.. 1 - myCount somehow gets rebound to a different/new MVar 2 - The binding stays the same but MVar gets reset to 0 without this being explicitly done in the code. I assume you mean the latter (2). But either case seems like an absurdity to me. No top level bindings randomly change halfway through a program and MVars (I hope) are not prone to random corruption (no need to suppose things are any different if they occur at the top level). But equally it can be implemented with IORefs, Actually it couldn't as IORefs are not an Ord instance. so it's not a good advert for the need for global variables. Oh please! We have to have something concrete to discuss and this is the simplest. Like I said there are a dozen or so other examples in the base package last time I counted and plenty of people have found that other libs/ffi bindings need them for safety reasons. Or at least they need something that has global main/process scope and so far the unsafePerformIO hack is the only known way to get that and still keep APIs stable,sane and modular. Also, AFAICS going the way that seems to be suggested of having all this stuff reflected in the arguments/types of API is going to make it practically impossible to have platform independent APIs if all platform specific implementation detail has to be accounted for in this way. The real irony of your remark is that making APIs this robust is practically impossible *without* using global variables, and you're now saying that because they've done this work to eliminate these constraints they now have to be held to account for this with an absurd API. I think there are two cases to consider here. A Data.Unique style library, which requires genuinely *internal* state, and which is agnostic to having multiple copies of itself loaded simultaneously. In that case, there is no requirement for a process-level scope for -, just that each instance of the library is only initialised once - the RTS can do this, as can any dynamic loader. The other is some library that really cannot be safely loaded multiple times, because it depends on some lower-level shared resource. Such a library simply cannot be made safe without cooperation from the thing that controls that shared resource, because you cannot prevent a second copy of it being loaded by something you have no control over. If the - proposal were only about supporting the first of these applications, I would have far fewer objections to it. But it would have nothing to do with process-level scope, then. The - proposal introduces no new problems that aren't already with us. It solves 1 problem in that at least there's no room for the compiler to get it wrong or for people do use dangerous things when using the unsafePerformIO hack. I think that is really the only problem that can be solved at the level of Haskell language definition. I also think we need to be careful about the use of the term process. IMO when we say the process defined by main, we are talking about an abstract process that is essentially defined by Haskell and may have nothing in common with a process as defined by various OS's (assuming there's an OS involved at all). Perhaps we should try be more clear and say Haskell process or OS process as appropriate. In particular when we say an MVar or IORef has global process scope (whether or not it occurs at top level) we are talking about a Haskell process, not an OS process. The issues you raise seem to me to be more to do with correct implementaton on various platforms using various tools of
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Adrian Hey wrote: We have to have something concrete to discuss and this is the simplest. Like I said there are a dozen or so other examples in the base package last time I counted and plenty of people have found that other libs/ffi bindings need them for safety reasons. Or at least they need something that has global main/process scope and so far the unsafePerformIO hack is the only known way to get that and still keep APIs stable,sane and modular. Actually all this use of the tainted and derogatory term global variable is causing me to be imprecise. All MVars/IORefs have global main/process scope whether or not they're bound to something at the top level. The purpose of the top level static binding is to prevent accidental or malicious state spoofing if it's important that the *same* IORef/MVar is always used for some purpose. Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Mon, 1 Sep 2008, Adrian Hey wrote: Actually all this use of the tainted and derogatory term global variable is causing me to be imprecise. All MVars/IORefs have global main/process scope whether or not they're bound to something at the top level. Global variable is exactly the right term to use, if we are following the terminology of other languages. We don't call the result of malloc/new etc a global variable, unless it is assigned to something with top-level scope. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Thu, Aug 28, 2008 at 07:21:48PM -0400, Brandon S. Allbery KF8NH wrote: OS provided one? What if you have an exokernel, where it is expected these things _will_ be implemented in the userspace code. why shouldn't that part of the exokernel be written in haskell? What's stopping it? Just wrap it in a state-carrying monad representing a context. That way you can also keep multiple contexts if necessary (and I think it is often necessary, or at least desirable, with most exokernel clients). That is exactly what I want to do, with the 'IO' monad. but I would like the IO primitives to be implementable in haskell _or_ C transparently and efficiently. It should not matter how the primitives are implemented. John -- John Meacham - ⑆repetae.net⑆john⑈ ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Fri, Aug 29, 2008 at 04:33:50PM -0700, Dan Weston wrote: C++ faced this very issue by saying that with global data, uniqueness of initialization is guaranteed but order of evaluation is not. Assuming that the global data are merely thunk wrappers over some common data source, this means that at minimum, there can be no data dependencies between plugins where the order of evaluation matters. Fortunately, we can do a whole lot better with haskell, the type system guarentees that order of evaluation is irrelevant :) no need to specify anything about implementations. John -- John Meacham - ⑆repetae.net⑆john⑈ ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Mon, 1 Sep 2008, John Meacham wrote: On Mon, Sep 01, 2008 at 10:45:05PM +0100, Ganesh Sittampalam wrote: Actually all this use of the tainted and derogatory term global variable is causing me to be imprecise. All MVars/IORefs have global main/process scope whether or not they're bound to something at the top level. Global variable is exactly the right term to use, if we are following the terminology of other languages. We don't call the result of malloc/new etc a global variable, unless it is assigned to something with top-level scope. global variable is not a very precise term in other languages for various platforms too a lot of times. for instance, windows dll's have the ability to share individual variables across all loadings of said dll. (for better or worse.) Interesting, is this just within a single process? Haskell certainly has more advanced scoping capabilities than other languages so we need a more refined terminology. I think 'IO scope' is the more precise term, as it implys the scope is that of the IO monad state. which may or may not correspond to some external 'process scope'. Hmm, to me that implies that if the IO monad stops and restarts, e.g. when a Haskell library is being called from an external library, then the scope stops and starts again (which I presume is not the intention of - ?) But I don't really care that much about the name, if there is consensus on what to call it that doesn't cause ambiguities with OS processes etc. Cheers, Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Sep 1, at 18:08, Ganesh Sittampalam wrote: On Mon, 1 Sep 2008, John Meacham wrote: On Mon, Sep 01, 2008 at 10:45:05PM +0100, Ganesh Sittampalam wrote: Actually all this use of the tainted and derogatory term global variable is causing me to be imprecise. All MVars/IORefs have global main/process scope whether or not they're bound to something at the top level. Global variable is exactly the right term to use, if we are following the terminology of other languages. We don't call the result of malloc/new etc a global variable, unless it is assigned to something with top-level scope. global variable is not a very precise term in other languages for various platforms too a lot of times. for instance, windows dll's have the ability to share individual variables across all loadings of said dll. (for better or worse.) Interesting, is this just within a single process? Last I checked, it was across processes; that is, every DLL has its own (optional) data segment which is private to the DLL but shared across all system-wide loaded instances of the DLL. This actually goes back to pre-NT Windows. Haskell certainly has more advanced scoping capabilities than other languages so we need a more refined terminology. I think 'IO scope' is the more precise term, as it implys the scope is that of the IO monad state. which may or may not correspond to some external 'process scope'. Hmm, to me that implies that if the IO monad stops and restarts, e.g. when a Haskell library is being called from an external library, then the scope stops and starts again (which I presume is not the intention of - ?) It tells me the flow of execution has temporarily exited the scope of the IO monad, but can return to it. The state is suspended, not exited. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Mon, 1 Sep 2008, Brandon S. Allbery KF8NH wrote: On 2008 Sep 1, at 18:08, Ganesh Sittampalam wrote: On Mon, 1 Sep 2008, John Meacham wrote: for instance, windows dll's have the ability to share individual variables across all loadings of said dll. (for better or worse.) Interesting, is this just within a single process? Last I checked, it was across processes; that is, every DLL has its own (optional) data segment which is private to the DLL but shared across all system-wide loaded instances of the DLL. This actually goes back to pre-NT Windows. Sounds like a recipe for fun :-) Haskell certainly has more advanced scoping capabilities than other languages so we need a more refined terminology. I think 'IO scope' is the more precise term, as it implys the scope is that of the IO monad state. which may or may not correspond to some external 'process scope'. Hmm, to me that implies that if the IO monad stops and restarts, e.g. when a Haskell library is being called from an external library, then the scope stops and starts again (which I presume is not the intention of - ?) It tells me the flow of execution has temporarily exited the scope of the IO monad, but can return to it. The state is suspended, not exited. In that case we could equally call the things library scope, as that's the only scope they're visible in unless exported. Anyway, as long as we're clear on what it means, the name doesn't really matter. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Mon, 1 Sep 2008, Adrian Hey wrote: Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Adrian Hey wrote: Eh? Please illustrate your point with Data.Unique. What requirements does it place on it's context? (whatever that might mean :-) It requires that its context initialises it precisely once. It's context being main? If so this is true, but I don't see why this is a problem. [...] Also ACIO monad properties guarantee that it's always initialised to the same value regardless of when this occurs. So I don't see the problem. You see this as a requirement that can be discharged by adding the ACIO concept; I see it as a requirement that should be communicated in the type. Another way of looking at it is that Data.Unique has associated with it some context in which Unique values are safely comparable. You want that context to always be the top-level/RTS scope, I would like the defining that context to be part of the API. Data.Unique is actually a poor example, as it is actually fine to initialise it multiple times as long as the resulting Unique values aren't treated as coming from the same datatype. I just don't see what you're getting at. There's no problem here and Data.Unique is not special. See the conversation with Ashley - you can have multiple copies of Data.Unique loaded without problem, as long as the resulting Unique datatypes aren't comparable with each other. myCount :: MVar Int myCount - newMVar 0 In a hypothetical second initialisation, do you mean.. 1 - myCount somehow gets rebound to a different/new MVar I mean this. Or, more precisely, that a *different* myCount gets bound to a different MVar. But equally it can be implemented with IORefs, Actually it couldn't as IORefs are not an Ord instance. Well, perhaps one could be added (along with hashing). Or perhaps it's not really needed; I don't know as I've never used Data.Unique, and I doubt I ever would as when I need a name supply I also want human readable names, and I can't think of any other uses for it, though no doubt some exist. so it's not a good advert for the need for global variables. Oh please! We have to have something concrete to discuss and this is the simplest. Like I said there are a dozen or so other examples in the base package last time I counted Would you mind listing them? It might help provide some clarity to the discussion. and plenty of people have found that other libs/ffi bindings need them for safety reasons. Or at least they need something that has global main/process scope and so far the unsafePerformIO hack is the only known way to get that and still keep APIs stable,sane and modular. Again, some specific examples would help. Also, AFAICS going the way that seems to be suggested of having all this stuff reflected in the arguments/types of API is going to make it practically impossible to have platform independent APIs if all platform specific implementation detail has to be accounted for in this way. It can all be wrapped up in a single abstract context argument; the only platform bleed would be if one platform needed a context argument but others didn't. I think there are two cases to consider here. A Data.Unique style library, which requires genuinely *internal* state, and which is agnostic to having multiple copies of itself loaded simultaneously. In that case, there is no requirement for a process-level scope for -, just that each instance of the library is only initialised once - the RTS can do this, as can any dynamic loader. The other is some library that really cannot be safely loaded multiple times, because it depends on some lower-level shared resource. Such a library simply cannot be made safe without cooperation from the thing that controls that shared resource, because you cannot prevent a second copy of it being loaded by something you have no control over. If the - proposal were only about supporting the first of these applications, I would have far fewer objections to it. But it would have nothing to do with process-level scope, then. The - proposal introduces no new problems that aren't already with us. It solves 1 problem in that at least there's no room for the compiler to get it wrong or for people do use dangerous things when using the unsafePerformIO hack. I think that is really the only problem that can be solved at the level of Haskell language definition. I just want to be clear that the second of the two categories above cannot be used to justify the proposal, as it does not make them safe. I also think we need to be careful about the use of the term process. IMO when we say the process defined by main, we are talking about an abstract process that is essentially defined by Haskell and may have nothing in common with a process as defined by various OS's (assuming there's an OS involved at all). Perhaps we should try be more clear and say Haskell process or OS process as appropriate. In particular
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: Well, the question of whether multiple copies of a module are ok is still open, I guess - as you say later, it seems perfectly reasonable for two different versions of Data.Unique to exist, each with their own types and global variables - so why not two copies of the same version, as long as the types aren't convertible? My feeling is that the the execution of - needs to follow the Data.Typeable instances - if the two types are the same according to Data.Typeable, then there must only be one - executed. They will be different types if they are in different package versions. Thus they could have different instances of Typeable. But why do we care about Typeable? So another question following on from that is what happens if there isn't any datatype that is an essential part of the module - with Unique, it's fine for there to be two -s, as long as the Uniques aren't compared. Does this kind of safety property apply elsewhere? It feels to me that this is something ACIO (or whatever it would be called after being changed) needs to explain. In the internal implementation of Unique, there must be only one MVar constructed with - per Unique type, i.e. per package version. This will work correctly, since values of Unique types from different package versions have different types, and thus cannot be compared. Unique values constructed at top level by - will also be unique and will work correctly. ua - newUnique ub - newUnique Here ua == ub will evaluate to False. I'd rather use Data.Typeable for this, and make sure (by whatever mechanism, e.g. compiler-enforced, or just an implicit contract) that the user doesn't break things with dodgy Typeable instances. You don't think that's rather ugly: a class that needs special deriving behaviour? I'd actually like to get rid of all special-case deriving: it should be for newtypes only. Implicit contract is worse. I really shouldn't be able to write coerce without referring to something marked unsafe or foreign. Have we stopped caring about soundness? In addition, one can only have one Typeable instance per type. By contrast, one can create multiple IOWitness values for the same type. For example, one can very easily create a system of open exceptions for IO, with an IOWitness value for each exception type, witnessing to the data that the exception carries. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
On Mon, 1 Sep 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: Well, the question of whether multiple copies of a module are ok is still open, I guess - as you say later, it seems perfectly reasonable for two different versions of Data.Unique to exist, each with their own types and global variables - so why not two copies of the same version, as long as the types aren't convertible? My feeling is that the the execution of - needs to follow the Data.Typeable instances - if the two types are the same according to Data.Typeable, then there must only be one - executed. They will be different types if they are in different package versions. Right, but they might be the same package version, if one is a dynamically loaded bit of code and the other isn't. Thus they could have different instances of Typeable. But why do we care about Typeable? Because of the coercion operation that follows from it. So another question following on from that is what happens if there isn't any datatype that is an essential part of the module - with Unique, it's fine for there to be two -s, as long as the Uniques aren't compared. Does this kind of safety property apply elsewhere? It feels to me that this is something ACIO (or whatever it would be called after being changed) needs to explain. In the internal implementation of Unique, there must be only one MVar constructed with - per Unique type, i.e. per package version. This will work correctly, since values of Unique types from different package versions have different types, and thus cannot be compared. Unique values constructed at top level by - will also be unique and will work correctly. My question was actually about what happens with some different library that needs -; how do we know whether having two -s is safe or not? I'd rather use Data.Typeable for this, and make sure (by whatever mechanism, e.g. compiler-enforced, or just an implicit contract) that the user doesn't break things with dodgy Typeable instances. You don't think that's rather ugly: a class that needs special deriving behaviour? I'd actually like to get rid of all special-case deriving: it should be for newtypes only. No, it seems like the right way to do introspection to me, rather than adding some new mechanism for describing a datatype as your paper suggests. Implicit contract is worse. I really shouldn't be able to write coerce without referring to something marked unsafe or foreign. Have we stopped caring about soundness? We could arrange for the class member of Typeable to be called unsafe In addition, one can only have one Typeable instance per type. By contrast, one can create multiple IOWitness values for the same type. For example, one can very easily create a system of open exceptions for IO, with an IOWitness value for each exception type, witnessing to the data that the exception carries. I don't see what the point of multiple values is, I'm afraid. A single instance of Typeable is fine for doing type equality tests. Cheers, Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: Right, but they might be the same package version, if one is a dynamically loaded bit of code and the other isn't. OK. It's up to the dynamic loader to deal with this, and make sure that initialisers are not run more than once when it loads the package into the RTS. The scopes and names are all well-defined. How hard is this? My question was actually about what happens with some different library that needs -; how do we know whether having two -s is safe or not? I don't understand. When is it not safe? No, it seems like the right way to do introspection to me, rather than adding some new mechanism for describing a datatype as your paper suggests. Aesthetic arguments are always difficult. The best I can say is, why are some classes blessed with a special language-specified behaviour? It looks like an ugly hack to me. We have a class with a member that may be safely exposed to call, but not safely exposed to define. How is this the right way? By contrast, top-level - is straightforward to understand. Even the scope issues are not hard. It's safe, it doesn't privilege a class with special and hidden functionality, it doesn't introspect into types, and it allows individual unique values rather than just unique instances per type. And it also allows top-level IORefs and MVars. We could arrange for the class member of Typeable to be called unsafe We could, but it's not actually unsafe to call as such. It's only unsafe to implement. And if we're going the implicit contract route, we have to resort to unsafe functions to do type representation. It's not necessary, and seems rather against the spirit of Haskell. Time was when people would insist that unsafePerformIO wasn't Haskell, though perhaps useful for debugging. Now we have all these little unsafe things because people think they're necessary, and there's an implicit contract forced on the user not to be unsafe. But it turns out that they're not necessary. I don't see what the point of multiple values is, I'm afraid. A single instance of Typeable is fine for doing type equality tests. Sometimes you want to do witness equality tests rather than type equality tests. For instance, I might have a foo exception and a bar exception, both of which carry an Int. Rather than create new Foo and Bar types, I can just create a new witness for each. Or if I want, I can create a dictionary of heterogeneous items, with IOWitness values as keys. Then I can do a top-level - to declare keys in this dictionary. Now I've got OOP objects. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Mon, 1 Sep 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: Right, but they might be the same package version, if one is a dynamically loaded bit of code and the other isn't. OK. It's up to the dynamic loader to deal with this, and make sure that initialisers are not run more than once when it loads the package into the RTS. The scopes and names are all well-defined. How hard is this? I have a feeling it might be non-trivial; the dynamically loaded bit of code will need a separate copy of the module in question, since it might be loaded into something where the module is not already present. So it'll have a separate copy of the global variable in a separate location, and the dynamic loader needs to arrange to do something weird, like copying the value of the first run - to the second one instead of running it again. My question was actually about what happens with some different library that needs -; how do we know whether having two -s is safe or not? I don't understand. When is it not safe? Well, the safety of - being run twice in the Data.Unique case is based around the two different Data.Unique types not being compatible. Let's suppose some other module uses a -, but returns things based on that - that are some standard type, rather than a type it defines itself. Is module duplication still safe? No, it seems like the right way to do introspection to me, rather than adding some new mechanism for describing a datatype as your paper suggests. Aesthetic arguments are always difficult. The best I can say is, why are some classes blessed with a special language-specified behaviour? Well, let me put it this way; since I don't like -, and I don't particularly mind Typeable, I wouldn't accept IOWitness as an example of something that requires - to implement correctly, because I don't see any compelling feature that you can only implement with -. We could arrange for the class member of Typeable to be called unsafe We could, but it's not actually unsafe to call as such. It's only unsafe to implement. That's fine, it can export a non-class member without the unsafe prefix. And if we're going the implicit contract route, we have to resort to unsafe functions to do type representation. It's not necessary, and seems rather against the spirit of Haskell. Time was when people would insist that unsafePerformIO wasn't Haskell, though perhaps useful for debugging. Now we have all these little unsafe things because people think they're necessary, and there's an implicit contract forced on the user not to be unsafe. But it turns out that they're not necessary. There's some unsafety somewhere in both Typeable and IOWitnesses, and in both cases it can be completely hidden from the user - with Typeable, just don't let the user define the typeOf function at all themselves. I'm not actually sure why it is exposed; is it necessary for some use pattern? I don't see what the point of multiple values is, I'm afraid. A single instance of Typeable is fine for doing type equality tests. Sometimes you want to do witness equality tests rather than type equality tests. For instance, I might have a foo exception and a bar exception, both of which carry an Int. Rather than create new Foo and Bar types, I can just create a new witness for each. This is precisely what newtype is designed for, IMO. We don't need another mechanism to handle it. Cheers, Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Dan Doel wrote: Here's a first pass: -- snip -- {-# LANGUAGE Rank2Types, GeneralizedNewtypeDeriving #-} module Unique where import Control.Monad.Reader import Control.Monad.Trans import Control.Concurrent.MVar -- Give Uniques a phantom region parameter, so that you can't accidentally -- compare Uniques from two different uniqueness sources. newtype Unique r = Unique Integer deriving Eq newtype U r a = U { unU :: ReaderT (MVar Integer) IO a } deriving (Functor, Monad, MonadIO) -- Higher rank type for region consistency runU :: (forall r. U r a) - IO a runU m = newMVar 0 = runReaderT (unU m) newUnique :: U r (Unique r) newUnique = U (do source - ask val - lift $ takeMVar source let next = val + 1 lift $ putMVar source next return $ Unique next) -- hashUnique omitted -- snip -- It's possible that multiple unique sources can exist in a program with this implementation, but because of the region parameter, the fact that a Unique may not be globally unique shouldn't be a problem. If your whole program needs arbitrary access to unique values, then I suppose something like: main = runU realMain realMain :: U r () realMain = ... is in order. Insert standard complaints about this implementation requiring liftIO all over the place if you actually want to do other I/O stuff inside the U monad. Well that wouldn't be my main complaint :-) Thanks for taking the time to do this Dan. I think the safety requirement has been met, but I think it fails on the improved API. The main complaint would be what I see as loss of modularity, in that somehow what should be a small irrelevant detail of the implementation of some obscure module somewhere has propogated it's way all the way upto main. This is something it seems to have in common with all other attempts I've seen to solve the global variable problem without actually using a..you know what :-) It doesn't matter whether it's explicit state handle args, withWhateverDo wrappers, novel monads or what. They all have this effect. To me this seems completely at odds with what I thought was generally accepted wisdom of how to write good maintainable, modular software. Namely hiding as much implemention detail possible and keeping APIs as simple and stable as they can be. I don't know if I'm alone in that view nowadays. I'm also not sure I understand why so many people seem to feel that stateful effects must be accounted for somehow in the args and/or types of the effecting function. Like if I had.. getThing :: IO Thing ..as an FFI binding, nobody would give it a moments thought. They'd see it from it's type that it had some mysterious world state dependent/effecting behaviour, but would be quite happy to just accept that the didn't really need to worry about all that magic... instead they'd accept that it just works. Why then, if I want to implement precisely the same thing in Haskell (using a global variable) does it suddenly become so important for this stateful magic to be accounted for? Like the presence of that global variable must be made so very painfully apparent in main (and everywhere else on the dependency path too I guess). In short, I just don't get it :-) Purists aren't going to like it, but I think folk *will* be using real global variables in I/O libs for the forseeable future. Seems a shame that they'll have to do this with unsafePerformIO hack though :-( Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Ashley Yakeley wrote: OK. Let's call it top-level scope. Haskell naturally defines such a thing, regardless of processes and processors. Each top-level - would run at most once in top-level scope. If you had two Haskell runtimes call by C code, each would have its own memory allocator and GC; IORefs, Uniques and thunks cannot be shared between them; and each would have its own top-level scope, even though they're in the same process. That sounds more feasible - though it does constrain a plugin architecture (in which Haskell code can dynamically load other Haskell code) to cooperate with the loading RTS and not load multiple copies of modules; this might make linking tricky. There's also the problem Duncan Coutts mentioned about loading multiple versions of the same module - what are the semantics of - in relation to that? Also, it's no use for mediating access to a resource or library that can only be accessed once, right? In fact, even without the problem of two Haskell runtimes in one process this can't work, since some library in another language might also choose to access that resource or library. What applications does this leave beyond Data.Unique and Random? Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Adrian Hey wrote: Ganesh Sittampalam wrote: Well, yes, but if I implemented a library in standard Haskell it would always be safely serialisable/deserialisable (I think). So the global variables hack somehow destroys that property - how do I work out why it does in some cases but not others? This has nothing to do with the use of global variables. If you have a set of values that are guaranteed to be distinct (unique) and you add another random/arbitrary value to that set you have no way of knowing that it is different from any current member (other than searching the entire set, assuming it's available). OK, never mind about this. I was thinking that referential transparency was violated by remoting, but since unique values can only be constructed in IO, I think I was wrong. Well, I've never seen a convincing use case for global variables :-) Well apart from all the libs that couldn't be implemented with them... They can't be implemented with an interface that is completely oblivious to the fact that the libraries require some state. Dynamic loading and plugins work fine with standard Haskell now, because nothing in standard Haskell breaks them. The - proposal might well break them, which is a significant downside for it. I don't see how, but if so - bindings are not the cause of the brokeness. They'd still be broken using the unsafePerformIO hack. Which places the unsafePerformIO hack at fault, seeing as it's unsafe and a hack and all :-) If - was standard then it'd be up to everyone else to work round its limitations. In general, the smaller the world that the Haskell standard lives in, the less it can interfere with other concerns. - massively increases that world, by introducing the concept of a process scope. All IORefs,MVars,Chans scope across the entire process defined by main. Or at least they *should*, if they don't then something is already badly wrong somewhere. This has nothing to do with whether or not they appear at top level. This is what an IORef/MVar whatever is defined to be. Their scope is where they can be used, and this is something we can explicitly track by inspecting the program text. If they are just used in one part of the program, their scope is limited to that part of the program. But then again, I'm sure that some that will be adamant that any way of making global variables is a hack. But they'll still be happy to go on using file IO, sockets etc regardless, blissfully unaware of the hacks they are dependent on :-) I'm not sure of precisely what you mean here, but stdin, stdout and stderr are things provided by the OS to a process. That's what defines them as having process scope, not something the Haskell language or RTS does. Those rules aren't actually strong enough to provide a guarantee of process level scope. The rules for - bindings shouldn't have to guarantee this. This should be guaranteed by newMVar returning a new *MVar*, wherever it's used (for example). The issue is whether the - is run multiple times in a single process or not, rather than how the thing it calls behaves. I mean semantic faults, as in the proposal just doesn't do what it promises for some subtle reason. It doesn't provide once-only semantics across an entire process in cases involving dynamic loading or two Haskell libraries together with RTS separately linked into the same C program. I don't know whether you intend that it does promise that or not, but it seems to be necessary for many of the applications that are used to justify it. If you consider not giving you thread local variables a fault I guess you're entitled to that view, but this was never the intent of the proposal in the first place (that's not what people are trying to do when they use the unsafePerformIO hack). The thread-local variables point was a relatively minor issue for me compared to the dynamic loading and related issues. Cheers, Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Aug 31, at 10:20, Ganesh Sittampalam wrote: On Sat, 30 Aug 2008, Adrian Hey wrote: But then again, I'm sure that some that will be adamant that any way of making global variables is a hack. But they'll still be happy to go on using file IO, sockets etc regardless, blissfully unaware of the hacks they are dependent on :-) I'm not sure of precisely what you mean here, but stdin, stdout and stderr are things provided by the OS to a process. That's what defines them as having process scope, not something the Haskell language or RTS does. But their representations in Haskell must have the same scope and are therefore de facto global variables. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:20, Ganesh Sittampalam wrote: I'm not sure of precisely what you mean here, but stdin, stdout and stderr are things provided by the OS to a process. That's what defines them as having process scope, not something the Haskell language or RTS does. But their representations in Haskell must have the same scope and are therefore de facto global variables. Yep, but this is not Haskell providing a way to make global variables, it is just providing an interface to ones that already exist. The point is that the RTS can't provide (process-scope) global variables of its own invention, because it can't guarantee to be running at the top-level of a process, which it needs to be in order to control their construction. Cheers, Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Aug 31, at 10:29, Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:20, Ganesh Sittampalam wrote: I'm not sure of precisely what you mean here, but stdin, stdout and stderr are things provided by the OS to a process. That's what defines them as having process scope, not something the Haskell language or RTS does. But their representations in Haskell must have the same scope and are therefore de facto global variables. Yep, but this is not Haskell providing a way to make global variables, it is just providing an interface to ones that already exist. The point is that the RTS can't provide (process-scope) But that is done the same way as providing general global variables, so you can't get away from it. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:29, Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:20, Ganesh Sittampalam wrote: I'm not sure of precisely what you mean here, but stdin, stdout and stderr are things provided by the OS to a process. That's what defines them as having process scope, not something the Haskell language or RTS does. But their representations in Haskell must have the same scope and are therefore de facto global variables. Yep, but this is not Haskell providing a way to make global variables, it is just providing an interface to ones that already exist. The point is that the RTS can't provide (process-scope) But that is done the same way as providing general global variables, so you can't get away from it. I don't follow what you mean. stdin, stdout and stderr are just file descriptors 0, 1 and 2, aren't they? You can create them as many times as you want with using that information without causing any confusion or conflict. Whereas the - proposal has a once-only requirement. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Aug 31, at 10:34, Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:29, Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:20, Ganesh Sittampalam wrote: I'm not sure of precisely what you mean here, but stdin, stdout and stderr are things provided by the OS to a process. That's what defines them as having process scope, not something the Haskell language or RTS does. But their representations in Haskell must have the same scope and are therefore de facto global variables. Yep, but this is not Haskell providing a way to make global variables, it is just providing an interface to ones that already exist. The point is that the RTS can't provide (process-scope) But that is done the same way as providing general global variables, so you can't get away from it. I don't follow what you mean. stdin, stdout and stderr are just file descriptors 0, 1 and 2, aren't they? You can create them as many times as you want with using that information without causing any confusion or conflict. Whereas the - proposal has a once-only requirement. The convention is to provide buffered versions to improve the performance of file I/O. These buffered filehandles must be created once per runtime instance (and ideally once per process so multiple runtimes don't find themselves overwriting each others' output). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sun, 31 Aug 2008, Adrian Hey wrote: Thanks for taking the time to do this Dan. I think the safety requirement has been met, but I think it fails on the improved API. The main complaint would be what I see as loss of modularity, in that somehow what should be a small irrelevant detail of the implementation of some obscure module somewhere has propogated it's way all the way upto main. That's the key point, as I see it - they aren't irrelevant details of the implementation, they are requirements the implementation places on its context in order for that implementation to be correct. So they should be communicated appropriately. To me this seems completely at odds with what I thought was generally accepted wisdom of how to write good maintainable, modular software. Namely hiding as much implemention detail possible and keeping APIs as simple and stable as they can be. I don't know if I'm alone in that view nowadays. It's no problem to hide implementation detail, but I don't think you should hide the *requirement* of the implementation that it has constraints on how it is called, namely that it requires once-only initialisation or whatever. Purists aren't going to like it, but I think folk *will* be using real global variables in I/O libs for the forseeable future. Seems a shame that they'll have to do this with unsafePerformIO hack though :-( From a purist point of view, it's a shame that they choose to do it at all :-) Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:34, Ganesh Sittampalam wrote: I don't follow what you mean. stdin, stdout and stderr are just file descriptors 0, 1 and 2, aren't they? You can create them as many times as you want with using that information without causing any confusion or conflict. Whereas the - proposal has a once-only requirement. The convention is to provide buffered versions to improve the performance of file I/O. These buffered filehandles must be created once per runtime instance (and ideally once per process so multiple runtimes don't find themselves overwriting each others' output). In that case it seems that any library that might be used from a runtime that isn't the top-level of a process should avoid doing IO to those handles, for fear of producing output corruption? Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Aug 31, at 10:44, Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:34, Ganesh Sittampalam wrote: I don't follow what you mean. stdin, stdout and stderr are just file descriptors 0, 1 and 2, aren't they? You can create them as many times as you want with using that information without causing any confusion or conflict. Whereas the - proposal has a once- only requirement. The convention is to provide buffered versions to improve the performance of file I/O. These buffered filehandles must be created once per runtime instance (and ideally once per process so multiple runtimes don't find themselves overwriting each others' output). In that case it seems that any library that might be used from a runtime that isn't the top-level of a process should avoid doing IO to those handles, for fear of producing output corruption? You handle it the same way you handle I/O with concurrency: either one of the runtimes is privileged to the extent that it owns the filehandles and other runtimes must make an inter-runtime call to use them, or the filehandle structures include locking and are shared across runtimes. Both of these are used in Haskell (see most GUI libraries for the former, and the implementation of Handles for the latter). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Adrian Hey wrote: Thanks for taking the time to do this Dan. I think the safety requirement has been met, but I think it fails on the improved API. The main complaint would be what I see as loss of modularity, in that somehow what should be a small irrelevant detail of the implementation of some obscure module somewhere has propogated it's way all the way upto main. That's the key point, as I see it - they aren't irrelevant details of the implementation, they are requirements the implementation places on its context in order for that implementation to be correct. So they should be communicated appropriately. Eh? Please illustrate your point with Data.Unique. What requirements does it place on it's context? (whatever that might mean :-) It just does what it says on the tin AFAICS. There are no requirements it places on clients (to use an OO term), as should any halfway decent API IMO. To me this seems completely at odds with what I thought was generally accepted wisdom of how to write good maintainable, modular software. Namely hiding as much implemention detail possible and keeping APIs as simple and stable as they can be. I don't know if I'm alone in that view nowadays. It's no problem to hide implementation detail, but I don't think you should hide the *requirement* of the implementation that it has constraints on how it is called, namely that it requires once-only initialisation or whatever. No decent API should require this. Data.Unique certainly doesn't. In fact is debatable whether any API should requre an initalisation call at all before other stuff should be called (the other stuff check initialisation has occured and do it itself if necessary). The real irony of your remark is that making APIs this robust is practically impossible *without* using global variables, and you're now saying that because they've done this work to eliminate these constraints they now have to be held to account for this with an absurd API. Seems like a clear case of no good deed going unpunished :-) Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Aug 31, at 11:20, Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 10:44, Ganesh Sittampalam wrote: In that case it seems that any library that might be used from a runtime that isn't the top-level of a process should avoid doing IO to those handles, for fear of producing output corruption? You handle it the same way you handle I/O with concurrency: either one of the runtimes is privileged to the extent that it owns the filehandles and other runtimes must make an inter-runtime call to use them, or the filehandle structures include locking and are shared across runtimes. Both of these are used in Haskell (see most GUI libraries for the former, and the implementation of Handles for the latter). Where do the filehandle structures live in the latter case? The place you clearly think so little of that you need to ask: process-global (or process-local depending on how you think about it) storage. And everything in that storage must have locking. (And this requirement makes it similar in some ways to other non-directly- accessible process state such as (say) the process id.) -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 11:20, Ganesh Sittampalam wrote: Where do the filehandle structures live in the latter case? The place you clearly think so little of that you need to ask: process-global (or process-local depending on how you think about it) storage. And everything in that storage must have locking. I'm sorry if this makes me seem ignorant, but I'd never heard of such a thing before. What is the API for accessing such storage, and/or where can I find its documentation? Cheers, Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sun, 31 Aug 2008, Adrian Hey wrote: Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Adrian Hey wrote: Thanks for taking the time to do this Dan. I think the safety requirement has been met, but I think it fails on the improved API. The main complaint would be what I see as loss of modularity, in that somehow what should be a small irrelevant detail of the implementation of some obscure module somewhere has propogated it's way all the way upto main. That's the key point, as I see it - they aren't irrelevant details of the implementation, they are requirements the implementation places on its context in order for that implementation to be correct. So they should be communicated appropriately. Eh? Please illustrate your point with Data.Unique. What requirements does it place on it's context? (whatever that might mean :-) It requires that its context initialises it precisely once. Data.Unique is actually a poor example, as it is actually fine to initialise it multiple times as long as the resulting Unique values aren't treated as coming from the same datatype. But equally it can be implemented with IORefs, so it's not a good advert for the need for global variables. The real irony of your remark is that making APIs this robust is practically impossible *without* using global variables, and you're now saying that because they've done this work to eliminate these constraints they now have to be held to account for this with an absurd API. I think there are two cases to consider here. A Data.Unique style library, which requires genuinely *internal* state, and which is agnostic to having multiple copies of itself loaded simultaneously. In that case, there is no requirement for a process-level scope for -, just that each instance of the library is only initialised once - the RTS can do this, as can any dynamic loader. The other is some library that really cannot be safely loaded multiple times, because it depends on some lower-level shared resource. Such a library simply cannot be made safe without cooperation from the thing that controls that shared resource, because you cannot prevent a second copy of it being loaded by something you have no control over. If the - proposal were only about supporting the first of these applications, I would have far fewer objections to it. But it would have nothing to do with process-level scope, then. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Aug 31, at 12:01, Ganesh Sittampalam wrote: On Sun, 31 Aug 2008, Brandon S. Allbery KF8NH wrote: On 2008 Aug 31, at 11:20, Ganesh Sittampalam wrote: Where do the filehandle structures live in the latter case? The place you clearly think so little of that you need to ask: process-global (or process-local depending on how you think about it) storage. And everything in that storage must have locking. You'll have to look at specific implementations. One that I can think of off the top of my head is Perl 5's ithreads; there is a distinguished allocation store which is global to all ithreads, and the interpreter instance gives you primitives for locking and mutexing (see use threads::shared;). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: On Sat, 30 Aug 2008, Ashley Yakeley wrote: OK. Let's call it top-level scope. Haskell naturally defines such a thing, regardless of processes and processors. Each top-level - would run at most once in top-level scope. If you had two Haskell runtimes call by C code, each would have its own memory allocator and GC; IORefs, Uniques and thunks cannot be shared between them; and each would have its own top-level scope, even though they're in the same process. That sounds more feasible - though it does constrain a plugin architecture (in which Haskell code can dynamically load other Haskell code) to cooperate with the loading RTS and not load multiple copies of modules; this might make linking tricky. This is a good idea anyway. It's up to the dynamic loading architecture to get this right. There's also the problem Duncan Coutts mentioned about loading multiple versions of the same module - what are the semantics of - in relation to that? If they are different versions, they ought to be considered different modules with different names. Thus, Unique in base-3.0.2.0 ought to be a different type than Unique in base-4.0. Thus any top-level initialisers ought to be considered different and be run separately. What's the current static behaviour? What happens if I link with packages B C, which link with different versions of A? Also, it's no use for mediating access to a resource or library that can only be accessed once, right? In fact, even without the problem of two Haskell runtimes in one process this can't work, since some library in another language might also choose to access that resource or library. What applications does this leave beyond Data.Unique and Random? So far we've just looked at declaring top-level IORefs and MVars. By declaring top-level values of type IOWitness, you can generate open witnesses to any type, and thus solve the expression problem. See my open witness library and paper: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/open-witness http://semantic.org/stuff/Open-Witnesses.pdf -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
On 2008-08-28, Yitzchak Gale [EMAIL PROTECTED] wrote: However we work that out, right now we need a working idiom to get out of trouble when this situation comes up. What we have is a hack that is not guaranteed to work. We are abusing the NOINLINE pragma and assuming things about it that are not part of its intended use. We are lucky that it happens to work right now in GHC. So my proposal is that, right now, we make the simple temporary fix of adding an ONLYONCE pragma that does have the proper guaranteed sematics. In the meantime, we can keep tackling the awkward squad. What keeps this a temporary fix. Even now, industrial user demands keep us from making radical changes to the languages and libraries. If we adopt a not entirely satisfactory solution, it's never going away. If we keep the NOINLINE pragma hack, we can claim it was never supported and do away with it. If we don't have a real solution, perhaps in this case we haven't worn the hair shirt long enough? -- Aaron Denney -- ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Adrian Hey wrote: Ganesh Sittampalam wrote: Will Data.Unique still work properly if a value is sent across a RPC interface? A value of type Unique you mean? This isn't possible. Data.Unique has been designed so cannot be Shown/Read or otherwise serialised/deserialised (for obvious reasons I guess). How do the implementers of Data.Unique know that they musn't let them be serialised/deserialised? What stops the same rule from applying to Data.Random? Also what if I want a thread-local variable? Well actually I would say that threads are bad concurrency model so I'm not keen on thread local state at all. Mainly because I'd like to get rid of threads, but also a few other doubts even if we keep threads. Even if you don't like them, people still use them. AFAICS this is irrelvant for the present discussions as Haskell doesn't support thread local variable thingies. If it ever does being precise about that is someone elses problem. The fact that your proposal isn't general enough to handle them is a mark against it; standardised language features should be widely applicable, and as orthogonal as possible to other considerations. For the time being the scope of IORefs/MVars/Chans is (and should remain) whatever process is described by main (whether or not they appear at top level). And if main isn't the entry point? This comes back to my questions about dynamic loading. (I.E. Just making existing practice *safe*, at least in the sense that the compiler ain't gonna fcuk it up with INLINING or CSE and every one understands what is and isn't safe in ACIO) Creating new language features means defining their semantics rather more clearly than just no inlining or cse, IMO. I wouldn't even know how to go about that to the satisfaction of purists. But global variables *are* being used whether or not the top level - bindings are implemented. They're in the standard libraries! So if this stuff matters someone had better figure it out :-) It's a hack that isn't robust in many situations. We should find better ways to do it, not standardise it. Cheers, Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: How do the implementers of Data.Unique know that they musn't let them be serialised/deserialised? Because if you could take a String and convert it to a Unique there would be no guarantee that result was *unique*. What stops the same rule from applying to Data.Random? Well the only data type defined by this is StdGen, which is a Read/Show instance. I guess there's no semantic problem with that (can't think of one off hand myself). Also what if I want a thread-local variable? Well actually I would say that threads are bad concurrency model so I'm not keen on thread local state at all. Mainly because I'd like to get rid of threads, but also a few other doubts even if we keep threads. Even if you don't like them, people still use them. AFAICS this is irrelvant for the present discussions as Haskell doesn't support thread local variable thingies. If it ever does being precise about that is someone elses problem. The fact that your proposal isn't general enough to handle them is a mark against it; standardised language features should be widely applicable, and as orthogonal as possible to other considerations. I think the whole thread local state thing is a complete red herring. I've never seen a convincing use case for it and I suspect the only reason these to issues have become linked is that some folk are so convinced that global variables are evil, they mistakenly think thread local variables must be less evil (because they are less global). Anyway, if you understand the reasons why all the real world libraries that do currently use global variables do this, it's not hard to see why they don't want this to be thread local (it would break all the safety properties they're trying to ensure). So whatever problem thread local variables might solve, it isn't this one. For the time being the scope of IORefs/MVars/Chans is (and should remain) whatever process is described by main (whether or not they appear at top level). And if main isn't the entry point? This comes back to my questions about dynamic loading. Well you're talking about some non-standard Haskell, so with this and other non standard stuff (like plugins etc) I guess the answer is it's up to whoever's doing this to make sure they do it right. I can't comment further as I don't know what it is they're trying to do, but AFAICS it's not a language design issue at present. If plugins breaks is down to plugins to fix itself, at least until such time as a suitable formal theory of plugins has been developed so it can become standard Haskell :-) (I.E. Just making existing practice *safe*, at least in the sense that the compiler ain't gonna fcuk it up with INLINING or CSE and every one understands what is and isn't safe in ACIO) Creating new language features means defining their semantics rather more clearly than just no inlining or cse, IMO. I wouldn't even know how to go about that to the satisfaction of purists. But global variables *are* being used whether or not the top level - bindings are implemented. They're in the standard libraries! So if this stuff matters someone had better figure it out :-) It's a hack that isn't robust in many situations. We should find better ways to do it, not standardise it. Nobody's talking about standardising the current hack. This the whole point of the top level - proposal, which JM seems to think is sound enough for incorporation into JHC (correctly IMO). Nobody's found fault with it, other than the usual global variables are evil mantra :-) Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: If you want to standardise a language feature, you have to explain its behaviour properly. This is one part of the necessary explanation. To be concrete about scenarios I was considering, what happens if: - the same process loads two copies of the GHC RTS as part of two completely independent libraries? For added complications, imagine that one of the libraries uses a different implementation instead (e.g. Hugs) - one Haskell program loads several different plugins in a way that allows Haskell values to pass across the plugin boundary How do these scenarios work with use cases for - like (a) Data.Unique and (b) preventing multiple instantiation of a sub-library? That's a good question. But before you propose these scenarios, you must establish that they are sane for Haskell as it is today. In particular, would _local_ IORefs work correctly? After all, the memory allocator must be global in some sense. Could you be sure that different calls to newIORef returned separate IORefs? Perhaps this is the One True Global Scope: the scope in which refs from newIORef are guaranteed to be separate. It's the scope in which values from newUnique are supposed to be different, and it would also be the scope in which top-level - would be called at most once. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Philippa Cowderoy wrote: Talking of which, we really ought to look at an IO typeclass or two (not just our existing MonadIO) and rework the library ops to use it in Haskell'. You're not the only one to want it, and if it's not fixed this time it may never get fixed. This could allow both the best of both worlds, as we could have a monad that one couldn't create global variables for, and a monad for which one could. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: If you want to standardise a language feature, you have to explain its behaviour properly. This is one part of the necessary explanation. To be concrete about scenarios I was considering, what happens if: - the same process loads two copies of the GHC RTS as part of two completely independent libraries? For added complications, imagine that one of the libraries uses a different implementation instead (e.g. Hugs) - one Haskell program loads several different plugins in a way that allows Haskell values to pass across the plugin boundary How do these scenarios work with use cases for - like (a) Data.Unique and (b) preventing multiple instantiation of a sub-library? That's a good question. But before you propose these scenarios, you must establish that they are sane for Haskell as it is today. In particular, would _local_ IORefs work correctly? After all, the memory allocator must be global in some sense. Could you be sure that different calls to newIORef returned separate IORefs? Yes, I would expect that. Allocation areas propagate downwards from the OS to the top-level of a process and then into dynamically loaded modules if necessary. Any part of this puzzle that fails to keep them separate (in some sense) is just broken. Perhaps this is the One True Global Scope: the scope in which refs from newIORef are guaranteed to be separate. Every single call to newIORef, across the whole world, returns a different ref. The same one as a previous one can only be returned once the old one has become unused (and GCed). It's the scope in which values from newUnique are supposed to be different, and it would also be the scope in which top-level - would be called at most once. I don't really follow this. Do you mean the minimal such scope, or the maximal such scope? The problem here is not about separate calls to newIORef, it's about how many times an individual - will be executed. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Adrian Hey wrote: Ganesh Sittampalam wrote: How do the implementers of Data.Unique know that they musn't let them be serialised/deserialised? Because if you could take a String and convert it to a Unique there would be no guarantee that result was *unique*. Well, yes, but if I implemented a library in standard Haskell it would always be safely serialisable/deserialisable (I think). So the global variables hack somehow destroys that property - how do I work out why it does in some cases but not others? I think the whole thread local state thing is a complete red herring. I've never seen a convincing use case for it and I suspect the only Well, I've never seen a convincing use case for global variables :-) reason these to issues have become linked is that some folk are so convinced that global variables are evil, they mistakenly think thread local variables must be less evil (because they are less global). I don't think they're less evil, just that you might want them for the same sorts of reasons you might want global variables. If plugins breaks is down to plugins to fix itself, at least until such time as a suitable formal theory of plugins has been developed so it can become standard Haskell :-) Dynamic loading and plugins work fine with standard Haskell now, because nothing in standard Haskell breaks them. The - proposal might well break them, which is a significant downside for it. In general, the smaller the world that the Haskell standard lives in, the less it can interfere with other concerns. - massively increases that world, by introducing the concept of a process scope. It's a hack that isn't robust in many situations. We should find better ways to do it, not standardise it. Nobody's talking about standardising the current hack. This the whole point of the top level - proposal, It just amounts to giving the current hack some nicer syntax and stating some rules under which it can be used. Those rules aren't actually strong enough to provide a guarantee of process level scope. which JM seems to think is sound enough for incorporation into JHC (correctly IMO). Nobody's found fault with it, other than the usual global variables are evil mantra :-) Several people have found faults with it, you've just ignored or dismissed them. No doubt from your perspective the faults are irrelevant or untrue, but that's not my perspective. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: Every single call to newIORef, across the whole world, returns a different ref. How do you know? How can you compare them, except in the same Haskell expression? The same one as a previous one can only be returned once the old one has become unused (and GCed). Perhaps, but internally the IORef is a pointer value, and those pointer values might be the same. From the same perspective, one could say that every single call to newUnique across the whole world returns a different value, but internally they are Integers that might repeat. It's the scope in which values from newUnique are supposed to be different, and it would also be the scope in which top-level - would be called at most once. I don't really follow this. Do you mean the minimal such scope, or the maximal such scope? The problem here is not about separate calls to newIORef, it's about how many times an individual - will be executed. Two IO executions are in the same global scope if their resulting values can be used in the same expression. Top-level - declarations must execute at most once in this scope. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: Every single call to newIORef, across the whole world, returns a different ref. How do you know? How can you compare them, except in the same Haskell expression? I can write to one and see if the other changes. The same one as a previous one can only be returned once the old one has become unused (and GCed). Perhaps, but internally the IORef is a pointer value, and those pointer values might be the same. From the same perspective, one could say that How can they be the same unless the memory management system is broken? I consider different pointers on different machines or in different virtual address spaces different too; it's the fact that they don't alias that matters. every single call to newUnique across the whole world returns a different value, but internally they are Integers that might repeat. The thing about pointers is that they are managed by the standard behaviour of memory allocation. This isn't true of Integers. In fact this point suggests an implementation for Data.Unique that should actually be safe without global variables: just use IORefs for the actual Unique values. IORefs already support Eq, as it happens. That gives you process scope for free, and if you want bigger scopes you can pair that with whatever makes sense, e.g. process ID, MAC address, etc. Two IO executions are in the same global scope if their resulting values can be used in the same expression. Top-level - declarations must execute at most once in this scope. This brings us back to the RPC question, and indeed to just passing values to somewhere else via FFI. I think you can work around some of that by talking about ADTs that aren't serialisable (e.g. ban the class Storable), but now we have different global scopes for different kinds of values, so which scope do we use to define - ? Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ashley Yakeley wrote: I don't really follow this. Do you mean the minimal such scope, or the maximal such scope? The problem here is not about separate calls to newIORef, it's about how many times an individual - will be executed. Two IO executions are in the same global scope if their resulting values can be used in the same expression. Top-level - declarations must execute at most once in this scope. Better: Two newIORef executions are in the same global scope if their resulting refs can be used in the same expression. Top-level - declarations must execute at most once in this scope. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: How do the implementers of Data.Unique know that they musn't let them be serialised/deserialised? What stops the same rule from applying to Data.Random? Unique values should be no more deserialisable than IORefs. Is it the functionality of Data.Unique that you object to, or the fact that it's implemented with a global variable? If the former, one could easily build Unique values on top of IORefs, since IORef is in Eq. Thus Data.Unique is no worse than IORefs (ignoring hashability, anyway). If the latter, how do you recommend implementing Data.Unique? Implementing them on IORefs seems ugly. Or should they just be a primitive of the platform, like IORefs themselves? -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Ashley Yakeley wrote: Is it the functionality of Data.Unique that you object to, or the fact that it's implemented with a global variable? If the former, one could easily build Unique values on top of IORefs, since IORef is in Eq. Thus Data.Unique is no worse than IORefs (ignoring hashability, anyway). If the latter, how do you recommend implementing Data.Unique? Implementing them on IORefs seems ugly. This seems fine to me. It's based on something that already does work properly across a process scope, instead of some new language feature that is actually hard to implement across the process scope. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: How can they be the same unless the memory management system is broken? I consider different pointers on different machines or in different virtual address spaces different too; it's the fact that they don't alias that matters. But the actual pointer value might repeat. every single call to newUnique across the whole world returns a different value, but internally they are Integers that might repeat. The thing about pointers is that they are managed by the standard behaviour of memory allocation. This isn't true of Integers. But it could be. A global variable allows us to do the same thing as the memory allocator, and allocate unique Integers just as the allocator allocates unique pointer values. Now you can say that the same pointer value on different machines is different pointers; equally, you can say the same Integer in Unique on different machines is different Uniques: it's the fact that they don't alias that matters. In fact this point suggests an implementation for Data.Unique that should actually be safe without global variables: just use IORefs for the actual Unique values. IORefs already support Eq, as it happens. That gives you process scope for free, Isn't this rather ugly, though? We're using IORefs for something that doesn't involve reading or writing to them. Shouldn't there be a more general mechanism? -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: This seems fine to me. It's based on something that already does work properly across a process scope, But you agree that IORefs define a concept of process scope? instead of some new language feature that is actually hard to implement across the process scope. If we have a concept of process scope, how is it hard to implement? -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: This seems fine to me. It's based on something that already does work properly across a process scope, But you agree that IORefs define a concept of process scope? I'm not sure that they *define* process scope, because it might be safe to use them across multiple processes; it depends on OS-dependent properties. But they exist *at least* at process scope. instead of some new language feature that is actually hard to implement across the process scope. If we have a concept of process scope, how is it hard to implement? Because memory allocation is already implemented, and not in a Haskell-dependent way. If two completely separate Haskell libraries are present in the same process, linked together by a C program, they don't even know about each others existence. But they still don't share memory space. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: On Sat, 30 Aug 2008, Adrian Hey wrote: Because if you could take a String and convert it to a Unique there would be no guarantee that result was *unique*. Well, yes, but if I implemented a library in standard Haskell it would always be safely serialisable/deserialisable (I think). So the global variables hack somehow destroys that property - how do I work out why it does in some cases but not others? This has nothing to do with the use of global variables. If you have a set of values that are guaranteed to be distinct (unique) and you add another random/arbitrary value to that set you have no way of knowing that it is different from any current member (other than searching the entire set, assuming it's available). Well, I've never seen a convincing use case for global variables :-) Well apart from all the libs that couldn't be implemented with them... reason these to issues have become linked is that some folk are so convinced that global variables are evil, they mistakenly think thread local variables must be less evil (because they are less global). I don't think they're less evil, just that you might want them for the same sorts of reasons you might want global variables. Global variables are needed to ensure important safety properties, but the only reasons I've seen people give for thread local variables is that explicit state threading is just so tiresome and ugly. Well that may be (wouldn't disagree), but I'm not aware of any library that simply couldn't be implemented without them. If plugins breaks is down to plugins to fix itself, at least until such time as a suitable formal theory of plugins has been developed so it can become standard Haskell :-) Dynamic loading and plugins work fine with standard Haskell now, because nothing in standard Haskell breaks them. The - proposal might well break them, which is a significant downside for it. I don't see how, but if so - bindings are not the cause of the brokeness. They'd still be broken using the unsafePerformIO hack. In general, the smaller the world that the Haskell standard lives in, the less it can interfere with other concerns. - massively increases that world, by introducing the concept of a process scope. All IORefs,MVars,Chans scope across the entire process defined by main. Or at least they *should*, if they don't then something is already badly wrong somewhere. This has nothing to do with whether or not they appear at top level. This is what an IORef/MVar whatever is defined to be. It's a hack that isn't robust in many situations. We should find better ways to do it, not standardise it. Nobody's talking about standardising the current hack. This the whole point of the top level - proposal, It just amounts to giving the current hack some nicer syntax and stating some rules under which it can be used. No, the unsafePerformIO hack is a hack because it's *unsound*. The compiler doesn't know how to translate this into code that does what the programmer intended. Fortunately ghc at least does have a couple of flags that give the intended result (we hope). The new binding syntax is nicer, but it's real purpose is to leave the compiler no wriggle room when interpreting the programmers intent. But then again, I'm sure that some that will be adamant that any way of making global variables is a hack. But they'll still be happy to go on using file IO, sockets etc regardless, blissfully unaware of the hacks they are dependent on :-) Those rules aren't actually strong enough to provide a guarantee of process level scope. The rules for - bindings shouldn't have to guarantee this. This should be guaranteed by newMVar returning a new *MVar*, wherever it's used (for example). which JM seems to think is sound enough for incorporation into JHC (correctly IMO). Nobody's found fault with it, other than the usual global variables are evil mantra :-) Several people have found faults with it, you've just ignored or dismissed them. No doubt from your perspective the faults are irrelevant or untrue, but that's not my perspective. I mean semantic faults, as in the proposal just doesn't do what it promises for some subtle reason. If you consider not giving you thread local variables a fault I guess you're entitled to that view, but this was never the intent of the proposal in the first place (that's not what people are trying to do when they use the unsafePerformIO hack). Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: On Sat, 30 Aug 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: This seems fine to me. It's based on something that already does work properly across a process scope, But you agree that IORefs define a concept of process scope? I'm not sure that they *define* process scope, because it might be safe to use them across multiple processes; it depends on OS-dependent properties. But they exist *at least* at process scope. How can one use IORefs across multiple processes? They cannot be serialised. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: On Sat, 30 Aug 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: This seems fine to me. It's based on something that already does work properly across a process scope, But you agree that IORefs define a concept of process scope? I'm not sure that they *define* process scope, because it might be safe to use them across multiple processes; it depends on OS-dependent properties. But they exist *at least* at process scope. How can one use IORefs across multiple processes? They cannot be serialised. Firstly, that's a property of the current implementation, rather than a universal one, IMO. I don't for example see why you couldn't add a newIORef variant that points into shared memory, locking issues aside. Also, the issue is not whether you can *use* them across multiple processes, but whether they are unique across multiple processes. Uniqueness has two possible definitions; aliasing, and representational equality. No two IORefs will ever alias, so by that definition they exist at global scope. For representational equality, that exists at least at process scope, and perhaps more. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: Firstly, that's a property of the current implementation, rather than a universal one, IMO. I don't for example see why you couldn't add a newIORef variant that points into shared memory, locking issues aside. OK, so that would be a new Haskell feature. And it's that feature that would be the problem, not top-level -. It would bring its own garbage collection issues, for instance. Currently we have shared memory should be raw bytes, and IORef values can't be serialised there. Also, the issue is not whether you can *use* them across multiple processes, but whether they are unique across multiple processes. Uniqueness has two possible definitions; aliasing, and representational equality. No two IORefs will ever alias, so by that definition they exist at global scope. For representational equality, that exists at least at process scope, and perhaps more. By global scope, I mean the largest execution scope an IORef created by newIORef can have. Each top-level IORef declaration should create an IORef at most once in this scope. IORefs cannot be serialised, so they cannot be sent over serialised RPC. So let us consider your shared memory possibility. Do you mean simply an IORef of a block of bytes of the shared memory? That would be fine, but that is really a different type than IORef. It still keeps the global scopes separate, as IORefs cannot be passed through [Word8]. Or do you mean you could use shared memory to pass across IORefs? This would mean joining the address spaces with no memory protection between them. It would mean joining the garbage collectors somehow. Once you've dealt with that, the issue of making sure that each initialiser runs only once for the new shared space is really only one more issue. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
On Sat, 30 Aug 2008, Ashley Yakeley wrote: Ganesh Sittampalam wrote: Firstly, that's a property of the current implementation, rather than a universal one, IMO. I don't for example see why you couldn't add a newIORef variant that points into shared memory, locking issues aside. OK, so that would be a new Haskell feature. And it's that feature that would be the problem, not top-level -. It would bring its own garbage collection issues, for instance. OK, never mind about that; I agree it's not a very good idea. An IORef shouldn't escape the scope of the RTS/GC that created it. Also, the issue is not whether you can *use* them across multiple processes, but whether they are unique across multiple processes. Uniqueness has two possible definitions; aliasing, and representational equality. No two IORefs will ever alias, so by that definition they exist at global scope. For representational equality, that exists at least at process scope, and perhaps more. By global scope, I mean the largest execution scope an IORef created by newIORef can have. Each top-level IORef declaration should create an IORef at most once in this scope. That's a reasonable definition, if by execution scope you mean your previous definition of where the IORef can be directly used. But it's not process scope; two independent Haskell libraries in the same process can no more share IORefs than two separate Haskell processes. [what I meant by global scope above was the entire world] Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Adrian Hey wrote: Global variables are needed to ensure important safety properties, but the only reasons I've seen people give for thread local variables is that explicit state threading is just so tiresome and ugly. Well that may be (wouldn't disagree), but I'm not aware of any library that simply couldn't be implemented without them. I thought I ought to say a bit more about my unkind and hasty words re. thread local variables. This is discussed from time to time and there's a wiki page here sumarising proposals... http://www.haskell.org/haskellwiki/Thread-local_storage One thing that worries me is that nobody seems to know what problem thread local storage is solving, hence diversity of proposals. I'm also a struggling to see why we need it, but I don't have any passionate objections to it either. Unfortunately for those of us that want a solution to the global variables problem the two issues seem have been linked as being the part of same problem, so while there's all this uncertainty about what thread local variables are actually going to be used for and what they should look like the (IMO) much simpler global variables problem/solution is in limbo. This has been going on 4 or 5 years now IIRC. But the global variables problem is really much simpler. All we want is something that does exactly what the unsafePerformIO hack currently does (assuming flag/pragma hackery does the trick), but does it reliably. (IMO, YMMV..) Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Aug 30, at 6:28, Adrian Hey wrote: Ganesh Sittampalam wrote: How do the implementers of Data.Unique know that they musn't let them be serialised/deserialised? Because if you could take a String and convert it to a Unique there would be no guarantee that result was *unique*. What stops the same rule from applying to Data.Random? Well the only data type defined by this is StdGen, which is a Read/ Show instance. I guess there's no semantic problem with that (can't think of one off hand myself). You *want* to be able to reproduce a given random seed, for simulations and the like. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: By global scope, I mean the largest execution scope an IORef created by newIORef can have. Each top-level IORef declaration should create an IORef at most once in this scope. That's a reasonable definition, if by execution scope you mean your previous definition of where the IORef can be directly used. But it's not process scope; two independent Haskell libraries in the same process can no more share IORefs than two separate Haskell processes. [what I meant by global scope above was the entire world] OK. Let's call it top-level scope. Haskell naturally defines such a thing, regardless of processes and processors. Each top-level - would run at most once in top-level scope. If you had two Haskell runtimes call by C code, each would have its own memory allocator and GC; IORefs, Uniques and thunks cannot be shared between them; and each would have its own top-level scope, even though they're in the same process. -- Ashley Yakeley ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Brandon S. Allbery KF8NH wrote: On 2008 Aug 28, at 20:45, Adrian Hey wrote: Lennart Augustsson wrote: If Haskell had always taken the pragmatic path of adding what seems easiest and most in line with imperative practice it would not be the language it is today. It would be Perl, ML, or Java. The Haskell philosophy has always been to stick it out until someone comes up with the right solution to a problem rather than picking some easy way out. BTW, unsafePerformIO seems quite pragmatic and easy to me, so let's not get too snobby about this. (Sorry, I couldn't resist.) It's anything but easy; there are specific rules you need to follow, including use of certain compiler pragmas, to insure it works properly. Yes, of course. The worst thing about all this is that the single most common use case AFAICS (the one under discussion) isn't even a safe use. Just pointing out that this pseudo function is certainly not something one would expect from an organisation as dedicated to the persuit of perfection as Lennart would have us believe. It's an expedient hack. Not that I wish to seem ungrateful or anything :-) Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On 2008 Aug 29, at 4:22, Adrian Hey wrote: Brandon S. Allbery KF8NH wrote: On 2008 Aug 28, at 20:45, Adrian Hey wrote: Lennart Augustsson wrote: If Haskell had always taken the pragmatic path of adding what seems easiest and most in line with imperative practice it would not be the language it is today. It would be Perl, ML, or Java. The Haskell philosophy has always been to stick it out until someone comes up with the right solution to a problem rather than picking some easy way out. BTW, unsafePerformIO seems quite pragmatic and easy to me, so let's not get too snobby about this. (Sorry, I couldn't resist.) It's anything but easy; there are specific rules you need to follow, including use of certain compiler pragmas, to insure it works properly. Yes, of course. The worst thing about all this is that the single most common use case AFAICS (the one under discussion) isn't even a safe use. Just pointing out that this pseudo function is certainly not something one would expect from an organisation as dedicated to the persuit of perfection as Lennart would have us believe. It's an expedient hack. Not that I wish to seem ungrateful or anything :-) ...but, as he noted, we *do* that until we find the right way to do it. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] [EMAIL PROTECTED] system administrator [openafs,heimdal,too many hats] [EMAIL PROTECTED] electrical and computer engineering, carnegie mellon universityKF8NH ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Brandon S. Allbery KF8NH wrote: On 2008 Aug 29, at 4:22, Adrian Hey wrote: Brandon S. Allbery KF8NH wrote: On 2008 Aug 28, at 20:45, Adrian Hey wrote: Lennart Augustsson wrote: If Haskell had always taken the pragmatic path of adding what seems easiest and most in line with imperative practice it would not be the language it is today. It would be Perl, ML, or Java. The Haskell philosophy has always been to stick it out until someone comes up with the right solution to a problem rather than picking some easy way out. BTW, unsafePerformIO seems quite pragmatic and easy to me, so let's not get too snobby about this. (Sorry, I couldn't resist.) It's anything but easy; there are specific rules you need to follow, including use of certain compiler pragmas, to insure it works properly. Yes, of course. The worst thing about all this is that the single most common use case AFAICS (the one under discussion) isn't even a safe use. Just pointing out that this pseudo function is certainly not something one would expect from an organisation as dedicated to the persuit of perfection as Lennart would have us believe. It's an expedient hack. Not that I wish to seem ungrateful or anything :-) ...but, as he noted, we *do* that until we find the right way to do it. So what's the problem with doing it *safely*, that is at least until someone has found the mythic right way to do it. Not that anybody has ever been able to offer any rational explanation of what's *wrong* with the current proposed solution AFAICS. Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Thu, 28 Aug 2008, Adrian Hey wrote: Ganesh Sittampalam wrote: On Thu, 28 Aug 2008, Adrian Hey wrote: There's no semantic difficulty with the proposed language extension, How does it behave in the presence of dynamic loading? To answer this you need to be precise about the semantics of what is being dynamically loaded. But this is too complex an issue for me to get in to right now. If you want to standardise a language feature, you have to explain its behaviour properly. This is one part of the necessary explanation. To be concrete about scenarios I was considering, what happens if: - the same process loads two copies of the GHC RTS as part of two completely independent libraries? For added complications, imagine that one of the libraries uses a different implementation instead (e.g. Hugs) - one Haskell program loads several different plugins in a way that allows Haskell values to pass across the plugin boundary How do these scenarios work with use cases for - like (a) Data.Unique and (b) preventing multiple instantiation of a sub-library? Actually as far as things like hs-plugins are concerned I'd alway meant one day what exactly a plugin is, semantically. But as I've never had cause to use them so never got round to it. Like is it a value, or does it have state and identity or what? Personally I think of them as values. I'm not sure what your questions about state and identity mean. If you don't have global variables, then state doesn't matter. What about remote procedure calls? Dunno, what problem do you anticipate? Will Data.Unique still work properly if a value is sent across a RPC interface? Also what if I want a thread-local variable? Well actually I would say that threads are bad concurrency model so I'm not keen on thread local state at all. Mainly because I'd like to get rid of threads, but also a few other doubts even if we keep threads. Even if you don't like them, people still use them. (I.E. Just making existing practice *safe*, at least in the sense that the compiler ain't gonna fcuk it up with INLINING or CSE and every one understands what is and isn't safe in ACIO) Creating new language features means defining their semantics rather more clearly than just no inlining or cse, IMO. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
C++ faced this very issue by saying that with global data, uniqueness of initialization is guaranteed but order of evaluation is not. Assuming that the global data are merely thunk wrappers over some common data source, this means that at minimum, there can be no data dependencies between plugins where the order of evaluation matters. Another C++ comparison is with a virtual base class, where A::B::D and A::C::D are supposed to be equal, irrespective of whether it was B or C that first called the constructor. In this case, some witness (a vtable) is necessary to ensure that this happens correctly. Dan Ganesh Sittampalam wrote: On Thu, 28 Aug 2008, Adrian Hey wrote: Ganesh Sittampalam wrote: On Thu, 28 Aug 2008, Adrian Hey wrote: There's no semantic difficulty with the proposed language extension, How does it behave in the presence of dynamic loading? To answer this you need to be precise about the semantics of what is being dynamically loaded. But this is too complex an issue for me to get in to right now. If you want to standardise a language feature, you have to explain its behaviour properly. This is one part of the necessary explanation. To be concrete about scenarios I was considering, what happens if: - the same process loads two copies of the GHC RTS as part of two completely independent libraries? For added complications, imagine that one of the libraries uses a different implementation instead (e.g. Hugs) - one Haskell program loads several different plugins in a way that allows Haskell values to pass across the plugin boundary How do these scenarios work with use cases for - like (a) Data.Unique and (b) preventing multiple instantiation of a sub-library? Actually as far as things like hs-plugins are concerned I'd alway meant one day what exactly a plugin is, semantically. But as I've never had cause to use them so never got round to it. Like is it a value, or does it have state and identity or what? Personally I think of them as values. I'm not sure what your questions about state and identity mean. If you don't have global variables, then state doesn't matter. What about remote procedure calls? Dunno, what problem do you anticipate? Will Data.Unique still work properly if a value is sent across a RPC interface? Also what if I want a thread-local variable? Well actually I would say that threads are bad concurrency model so I'm not keen on thread local state at all. Mainly because I'd like to get rid of threads, but also a few other doubts even if we keep threads. Even if you don't like them, people still use them. (I.E. Just making existing practice *safe*, at least in the sense that the compiler ain't gonna fcuk it up with INLINING or CSE and every one understands what is and isn't safe in ACIO) Creating new language features means defining their semantics rather more clearly than just no inlining or cse, IMO. Ganesh ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
On Fri, Aug 29, 2008 at 4:33 PM, Dan Weston [EMAIL PROTECTED] wrote: C++ faced this very issue by saying that with global data, uniqueness of initialization is guaranteed but order of evaluation is not. In C++ circles, this is referred to as the static initialization order fiasco, and it is a frequent cause of crashes that are very difficult to debug. http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 I think it would be fair to say that C++ pushed this problem off to every user of the language. I haven't seen a coherent description of what the semantics of top-level - should be, but avoidance of widespread swearing would be at the top of my list of requirements. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
I actually was more interested in the problems with the obvious fix for this, namely the construct on first use idiom: int A(int a) { static int aa = a; return aa; } int B() { return A(3); } int C() { return A(7); } int D() { if (today() == Tuesday) B(); else C(); return a(0); } What is the value of D? Notice that this is never a problem with pure functions. The problem is that today() makes this an IO monad, and the swearing starts again. Dan Bryan O'Sullivan wrote: On Fri, Aug 29, 2008 at 4:33 PM, Dan Weston [EMAIL PROTECTED] wrote: C++ faced this very issue by saying that with global data, uniqueness of initialization is guaranteed but order of evaluation is not. In C++ circles, this is referred to as the static initialization order fiasco, and it is a frequent cause of crashes that are very difficult to debug. http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 I think it would be fair to say that C++ pushed this problem off to every user of the language. I haven't seen a coherent description of what the semantics of top-level - should be, but avoidance of widespread swearing would be at the top of my list of requirements. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Ganesh Sittampalam wrote: Will Data.Unique still work properly if a value is sent across a RPC interface? A value of type Unique you mean? This isn't possible. Data.Unique has been designed so cannot be Shown/Read or otherwise serialised/deserialised (for obvious reasons I guess). Also what if I want a thread-local variable? Well actually I would say that threads are bad concurrency model so I'm not keen on thread local state at all. Mainly because I'd like to get rid of threads, but also a few other doubts even if we keep threads. Even if you don't like them, people still use them. AFAICS this is irrelvant for the present discussions as Haskell doesn't support thread local variable thingies. If it ever does being precise about that is someone elses problem. For the time being the scope of IORefs/MVars/Chans is (and should remain) whatever process is described by main (whether or not they appear at top level). (I.E. Just making existing practice *safe*, at least in the sense that the compiler ain't gonna fcuk it up with INLINING or CSE and every one understands what is and isn't safe in ACIO) Creating new language features means defining their semantics rather more clearly than just no inlining or cse, IMO. I wouldn't even know how to go about that to the satisfaction of purists. But global variables *are* being used whether or not the top level - bindings are implemented. They're in the standard libraries! So if this stuff matters someone had better figure it out :-) Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Bryan O'Sullivan wrote: I haven't seen a coherent description of what the semantics of top-level - should be, but avoidance of widespread swearing would be at the top of my list of requirements. Don't the ACIO monad properties satisfy you? Anyway, as I pointed out in my last post, if this is a problem with top level - ACIO monad bindings it's still going to be a problem (probably much worse) with unsafePerformIO hack IO monad bindings. This problem isn't just going to go away, no matter how long it's ignored :-) Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
I'm certain you can write a kernel in Haskell where the only use of global variables is those that hardware interfacing forces you to use. On Thu, Aug 28, 2008 at 3:32 AM, John Meacham [EMAIL PROTECTED] wrote: On Thu, Aug 28, 2008 at 12:15:10AM +0100, Lennart Augustsson wrote: I didn't say NetBSD doesn't use global variables, I said the device driver model doesn't use global variables. And quite a few things that used to be in global variables have been moved into allocated variables and are being passed around instead. That's simply a better way to structure the code. Indeed. I have experimented with single address space operating systems where it is pretty much the only way to do things at the user level. But I still want to be able to implement my kernel in haskell. :) I don't don't think global variables should be banned, I just think they should be severly discouraged. Oh, I certainly agree with that. especially among new programmers. I think ACIO is a particularly elegant way to provide them in haskell for when they are needed. Every time I can avoid resorting to C code for a task without sacrificing performance, aethetics, or correctness, it is a good day. John -- John Meacham - ⑆repetae.net⑆john⑈ ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re[2]: [Haskell-cafe] Re: [Haskell] Top Level -
Hello Lennart, Thursday, August 28, 2008, 12:00:41 PM, you wrote: I'm certain you can write a kernel in Haskell where the only use of global variables is those that hardware interfacing forces you to use. moreover, you can write it in Turing machine. the question is just how comfortable it will be :) having an experience of writing medium-size real-world app, i have to say that global vars make life much easier - it's a pain to pass all the refs across all the function boundaries. OTOH they are headache - the only parts of program that can't be instantiated many times in concurrent threads are those using global vars so, for me global vars is a must - sometimes you just don't have time budget to make aesthetic design and anyway adding a lot of vars to every function you wrote doesn't look very beautiful. OTOH it's better to limit their usage, but this decision should be made by user, not enforced by language. at practice, ghc already provides the way to make global vars, we say only about making this feature simpler and more standard -- Best regards, Bulatmailto:[EMAIL PROTECTED] ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Top Level -
Jonathan Cast wrote: On Wed, 2008-08-27 at 11:53 +0100, Adrian Hey wrote: John Meacham wrote: As with all design decisions, it is sometimes the right thing and sometimes the wrong one. And sometimes the most expedient. (which, occasionally, is a perfectly valid driving force behind a certain bit of coding). However, I am fully convinced it is necessary. You don't even have to look further than Haskell 98 to find a use in the Random module, and Data.Unique _depends_ on the state being global for correctness. ..and of course there's stdin, stdout. That takes some explaining. Not really. If you don't have buffered IO, then you just say stdin = 0 stdout = 1 stderr = 2 nonStdout = 42? I'm afraid I have no idea what your point is :-( I tried it anyway and doesn't seem to work, but that ain't so surprising as Handles aren't Nums. What needs explaining IMO is that we appear to have global Handles exported at the top level from System.IO, but no way for users to write their own modules that do the same for nonStdout, or even to implement getNonStdout. I think that's pretty weird and inconsistent. But perhaps you could show me how to do it with some real Haskell :-) If you need buffered IO, you just change your IO monad* to look like: newtype NewIO alpha = NewIO (ReaderT (Map Fd Buffer) OldIO alpha) Of course, if you do this, you can't go mixing IO with unique values with RNG with mutable state with everything else under the sun anymore. You might actually have to declare exactly what effects you need when you give your function's type, now. Clearly, a horror we must avoid at all costs. Indeed. If anyone thinks that's the way to go maybe Clean would be of some interest. IMHO Cleans treatment of IO and concurrency is just about the worst thing in an otherwise pretty decent language :-( Regards -- Adrian Hey ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe