Re: [Haskell-cafe] global variables

[cc'ing HPrime] Isaac Dupree wrote:
The unsafePerformIO hack being used is not very satisfactory given how many optimizations make it difficult to use safely in practice. This hack is also used many places. I would be happier if that situation were not true, and I suspect there's something like a consensus on _that_. (maybe not as strong as "_needs_ a solution" in the short-to-mid term future)
Considering the value that the Haskell community normally places on sound semantics, reliance on such an appalling hack seems pretty bad to me. If a solution doesn't find it's way into H' then how many more years is it going to be with us? It's just embarrassing :-) Also, I don't know if the OP was a noob, but telling people (especially noobs) that if they can't figure out how to solve a problem without using a "global variable" then that must be down to inexperience and general cluelessness on their part just seems wrong to me. It simply isn't true. (Anyone who disagrees with this should feel free to submit the patches needed to fix up the base package :-) Regards -- Adrian Hey

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Adrian Hey wrote:
[cc'ing HPrime]
Isaac Dupree wrote:
The unsafePerformIO hack being used is not very satisfactory given how many optimizations make it difficult to use safely in practice. This hack is also used many places. I would be happier if that situation were not true, and I suspect there's something like a consensus on _that_. (maybe not as strong as "_needs_ a solution" in the short-to-mid term future)
Considering the value that the Haskell community normally places on sound semantics, reliance on such an appalling hack seems pretty bad to me. If a solution doesn't find it's way into H' then how many more years is it going to be with us? It's just embarrassing :-)
Yes, also it places value on REALLY EXTREMELY (excessively?) SOUND semantics, and on the modularity of the language even more than the modularity of its uses (or something like that :-) Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step... if successful it would remove the occasional need for -fno-cse for a whole module in GHC, at least. Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGUF4yHgcxvIWYTTURAvqWAJ46eFRt5LK1lUwqr2BmHVSrHljxzwCfYGJB x5ivAFEw5vYKbxTPIg+PrIU= =0xVK -----END PGP SIGNATURE-----

Isaac Dupree wrote:
Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step... if successful it would remove the occasional need for -fno-cse for a whole module in GHC, at least.
I have a hard time trying to understand why anyone would prefer this to the simple and clear <- syntax that's been proposed. As for the ACIO monad itself, this is utterly trivial and requires no language change. It's just a library. Maybe the first pragma you propose might have other uses to control optimisations, so I'm not totally anti this. But generally I dislike pragmas (I always find myself wondering what's wrong with the language design that makes the pragma necessary). So pragmas that influence optimisation are something I can live with. But using pragmas to influence *semantics* really is an evil practice IMO and is something that should be discouraged, not made an unavoidable necessity. But yes, if this problem isn't going to be properly addressed then at the very least the -fno-cse flag or something similar needs standardising (NOINLINE already is I think). Or we port all existing unsafePerfomIO hacked code to use Johm Meachams variant of the hack (uses types to ensure the compiler doesn't see common sub-expressions). Regards -- Adrian Hey

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Adrian Hey wrote:
Isaac Dupree wrote:
Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step... if successful it would remove the occasional need for -fno-cse for a whole module in GHC, at least.
I have a hard time trying to understand why anyone would prefer this to the simple and clear <- syntax that's been proposed. As for the ACIO monad itself, this is utterly trivial and requires no language change. It's just a library.
Maybe the first pragma you propose might have other uses to control optimisations, so I'm not totally anti this. But generally I dislike pragmas (I always find myself wondering what's wrong with the language design that makes the pragma necessary).
So pragmas that influence optimisation are something I can live with. But using pragmas to influence *semantics* really is an evil practice IMO and is something that should be discouraged, not made an unavoidable necessity.
Indeed. My rationale: - It would get some reliable semantics implemented in GHC (and/or other compilers hopefully). Since what we have already is a multi-part hack, this might be a nontrivial/important piece of work, and should make such things more reliable. - Pragmas (NOINLINE) are already used to influence semantics here. This idea doesn't introduce anything "worse" than that. And it doesn't require that people subscribe to particular syntax, ACIO implementation, etc. - Once implemented, if I understand correctly (do I?), it should make it easier for non-Simon to try out the hard work of a "real" solution involving non-pragma-syntax changes, ACIO libraries, or whatever is desired. Not because I think it's a great solution (nor even deserve to be called a real "solution" at all), but because nothing is being implemented now, for whatever reason. So I'm putting out this idea, in case it's a step in the right direction that someone is willing to take. Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGUJlEHgcxvIWYTTURAiGRAJ9ovzlD1Tc/Ce5tbCbYBBGcWLX/9ACfYzc3 a+xC3hQrXB3V9Iq+0vzxnmg= =EGk7 -----END PGP SIGNATURE-----

Hello Isaac, Sunday, May 20, 2007, 6:41:54 PM, you wrote:
Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step...
or LOOK_BUT_DON'T_TOUCH :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Isaac Dupree wrote:
Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step... if successful it would remove the occasional need for -fno-cse for a whole module in GHC, at least.
ISOLATE, DON'T_OPTIMIZE are actually bad names for the whole effect, which requires persistent CAF semantics. An implementation that doesn't make top-level definitions be CAFs, or even one that is willing to garbage-collect them when memory is tight such that they need recalculation later, would need a special case for global variables to make them work. i.e. I'm not sure if there exists a reasonable pragma while the code still uses unsafePerformIO. Hmm.... How about so, {-# NOINLINE var #-} var :: IORef Int var = unsafePerformIO (newIORef 3) - --> var :: IORef Int var = {-# EVALUATE_THIS_TEXT_ONLY_ONCE #-} (unsafePerformIO (newIORef 3)) to capture the desired semantics: text-based uniqueness, no duplication, no sharing of the IORefs (sharing the pure contents is fine), and no need to actually evaluate it any times at all. {-# EVALUATE_THIS_TEXT_ONLY_ONCE #-} is syntactically like a (special) function. Clearly it is an impossible demand for polymorphic things, so the compiler could complain (at least a warning) if the (var :: IORef Int) line was left off, for example. I guess it would also complain about non-type(class) argument dependencies too such as (f x = (unsafePerformIO (newIORef (x::Int))) )... Food for thought :-) Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGU02YHgcxvIWYTTURAoCaAKCkDH7Pd7JbNt0TmNig9j7ujiUV9ACZAevI QOjdmMbrPfVrKBafZshCh7c= =9/5v -----END PGP SIGNATURE-----

Isaac Dupree wrote:
var :: IORef Int var = {-# EVALUATE_THIS_TEXT_ONLY_ONCE #-} (unsafePerformIO (newIORef 3))
I think I still prefer.. var :: IORef Int var <- newIORef 3 or, more likely.. var :: IORef Int var <- ACIO.newIORef 3 The <- syntax should make the intended semantics clear and unambiguous, so it becomes the problem of individual implementors (not standards writers) to make sure that whatever optimisations or transformations that may be appropriate for their implementation preserve those semantics. (IOW there's no need to worry about what a pragma really means in operational terms, AFAICS). The ACIO monad also restricts what programmers may use on the rhs of the <-. But if you want a good name for the pragma how about this..
var :: IORef Int var = {-# <- #-} (unsafePerformIO (newIORef 3))
:-) Regards -- Adrian Hey

On 5/23/07, Adrian Hey
I think I still prefer..
var :: IORef Int var <- newIORef 3
So do I. For one very good reason: this syntax could be defined as a
"constructor" syntax and guaranteed to run before main.
The other syntaxes proposed don't strike me as sufficiently rigorous.
--
Taral

Taral wrote:
On 5/23/07, Adrian Hey
wrote: I think I still prefer..
var :: IORef Int var <- newIORef 3
So do I. For one very good reason: this syntax could be defined as a "constructor" syntax and guaranteed to run before main.
Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all). But anyway, the constraints of the ACIO monad allow creation to occur at any time before the first attempt to read or write the IORef.
The other syntaxes proposed don't strike me as sufficiently rigorous.
Me neither. It's always been a great source of puzzlement to me why this very simple and IMO conservative proposal should be so controversial. Unless someone can point out some severe semantic difficulty or suggest something better it seems like a no-brainer to me. Regards -- Adrian Hey

On 5/24/07, Adrian Hey
Taral wrote:
The other syntaxes proposed don't strike me as sufficiently rigorous.
Me neither. It's always been a great source of puzzlement to me why this very simple and IMO conservative proposal should be so controversial. Unless someone can point out some severe semantic difficulty or suggest something better it seems like a no-brainer to me.
I think it lacks implementation. I don't have time, or I'd look into
hacking this into GHC.
--
Taral

Adrian Hey wrote:
Taral wrote:
On 5/23/07, Adrian Hey
wrote: I think I still prefer..
var :: IORef Int var <- newIORef 3
So do I. For one very good reason: this syntax could be defined as a "constructor" syntax and guaranteed to run before main.
Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all). But anyway, the constraints of the ACIO monad allow creation to occur at any time before the first attempt to read or write the IORef.
The other syntaxes proposed don't strike me as sufficiently rigorous.
Me neither. It's always been a great source of puzzlement to me why this very simple and IMO conservative proposal should be so controversial. Unless someone can point out some severe semantic difficulty or suggest something better it seems like a no-brainer to me.
This is going to be highly subjective, but to me it still doesn't feel like it falls under the bar for implementation cost given its usefulness. The new syntax requires additions all the way through the front end of the compiler: parser, abstract syntax, renamer, type checker, desugarer, for something that is rarely used. It's a first-class language construct (a new top-level binding form, no less), and it has to pay its way. Also you want to add the ACIO monad as a built-in to the language. Not that my gut feeling should in any way be considered the final word on the subject, but I thought I should say something about why we're not running to implement this right now. To me it seems like we should let it simmer some more. Cheers, Simon

On 24/05/07, Adrian Hey
Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all).
You can allocate heap space at compile time? (Well, I guess you could, but that wouldn't still be usable at run time...) I imagine newIORef as mallocing() some room, then returning a pointer to that memory. That doesn't seem like something that could be done at compile time. -- -David House, dmhouse@gmail.com

David House wrote:
On 24/05/07, Adrian Hey
wrote: Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all).
You can allocate heap space at compile time? (Well, I guess you could, but that wouldn't still be usable at run time...) I imagine newIORef as mallocing() some room, then returning a pointer to that memory. That doesn't seem like something that could be done at compile time.
There seems to be quite a few implicit (and incorrect) assumptions in your argument, which is fallacious IMO. The logic of your argument would imply that *no* top level expression can be evaluated at compile time. This might be the case with ghc, though I doubt it (and even if it was this would just be a ghc problem). BTW, the Haskell standard says nothing about any kind of heap, let alone a C style malloc. Regards -- Adrian Hey

On 2007-05-24, David House wrote:
On 24/05/07, Adrian Hey
wrote: Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all).
You can allocate heap space at compile time? (Well, I guess you could, but that wouldn't still be usable at run time...) I imagine newIORef as mallocing() some room, then returning a pointer to that memory. That doesn't seem like something that could be done at compile time.
You can allocate bss or data space at compile time for the executable you are compiling. (Well, if you read compile as compile and link. It's a bit fuzzy.) -- Aaron Denney -><-

Aaron Denney wrote:
On 2007-05-24, David House wrote:
On 24/05/07, Adrian Hey
wrote: Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all). You can allocate heap space at compile time? (Well, I guess you could, but that wouldn't still be usable at run time...) I imagine newIORef as mallocing() some room, then returning a pointer to that memory. That doesn't seem like something that could be done at compile time.
You can allocate bss or data space at compile time for the executable you are compiling. (Well, if you read compile as compile and link. It's a bit fuzzy.)
Well we don't need to get too bogged down with the details of how any particular compiler/linker/rts might work. The point being that with any.. myIORef <- newIORef initialExpression whether or not it's at the top level, the only information needed to create the IORef is the initialExpression, and if it's at the top level then this is available at compile time (it doesn't even have to be evaluated at compile time in order to create the IORef). But it doesn't require any information from, nor should it have any effect on, the outside world that an executing program is interacting with. It is conceivable that for some newIORef implementations this would not be true, but in that case it's difficult to see how such implementations could safely put their newIORef in the ACIO monad anyway. Regards -- Adrian Hey
participants (7)
-
Aaron Denney
-
Adrian Hey
-
Bulat Ziganshin
-
David House
-
Isaac Dupree
-
Simon Marlow
-
Taral