[Haskell-cafe] Current situation regarding global IORefs
ahey at iee.org
Thu Apr 27 06:09:58 EDT 2006
Lennart Augustsson wrote:
> I was going to respond, but Cale very eloquently said most
> of what I was thinking.
I don't think eloquent is the word I would use, but I'm certainly
glad you didn't feel the need to repeat all that. It'd be really nice
if just for once the "global mutable state is evil" folk could give
unsafePerformIO hack users the benefit of the doubt, not start
out from the presumption that we are a bunch of lazy incompetent
retards who are badly in need of education in the rudiments of
the IO monad and (allegedly) good programming practice in general.
These arguments about the language needing to protect users from
doing dangerous things (specifically creating top level mutable
state) are quite wrong headed. Concurrency can be dangerous too,
so should we lose it? Top level mutable state can be a way of
*gaining safety*, not losing it.
What really frustrates me about all this is that AFAIK there are no
significant technical or theoretical reasons why we can't get
this safety (without resort to the unsafePerformIO hack). The
only serious obstacle seems political, with this very strange but
apparently widespread dogma about so called "global variables"
being inherently evil, regardless of the circumstances or purpose
for which they are used.
With regard to Roberts post, I don't want too say much other than
Robert is the first person to provide an answer to my question.
I hope I'm not mis-representing his views, but I believe Robert
objects to the existence of IO libraries that could not be implemented
in Haskell (in principle). I.E. those that implicitly reference top
level mutable state. I might be missing something, but AFAICS just
about all the current IO libraries fall into this category.
To take a specific example, look at the socket API. None of the
functions there take any kind of OS or network sub-system state handle
as an explicit argument. So it seems to me that either the
implementation is entirely stateless, all the way down to peeking and
poking the registers of Ethernet MACs, DMA controllers and wotnot
(unlikely), or it's sneakily accessing top level mutable state in some
extremely devious and non-transparent manner (heaven forbid).
Or put another way, would it be possible to implement the socket
API, exactly as it currently is, entirely in Haskell, starting with
nothing but hardware? I don't believe it is possible, but perhaps
somebody can show me I'm wrong.
> Let me just add one thing. Sometimes you hear the argument
> "I need a global IORef here because it's to track the use of my
> single screen" (or keyboard, or elevator, or some some
> other gizmo in th real world).
No, this is not the justification for the creation of top level TWI's.
This is the justification for not requiring that the API that mutates
a particular top level TWI state takes that state as an explicit
argument. There's no point if there is (and can be) only one of them.
This is why you don't have to pass an OS state handle to every IO
function that interacts with "the" OS (note singular).
But even if there are two or more, you still need some mechanism
to ensure that you have precisely 1:1 correspondance between
physical devices and device state TWI's and/or device driver
threads. This more or less prevents a robust API allowing unconstrained
creation of new device state TWI's. It's far safer and simpler
to provide top level TWIs (state handles) as *abstract data types*
(not IORefs!). This is no different from (or less safe than) having
stdout appear at the top level.
Even if there is an unknown (at compile time) number of such
devices and instead they are discovered somehow at boot time,
you still need to maintain a some kind of finite pool of these
device states, which is itself necessarily a unique TWI.
IME, the approach you take to these kinds of problems can vary
depending what you know or don't know for certain about the
system your working with. But you always end up using top level
mutable state somewhere along the way. I can only assume folk who
insist it's unnecessary (or worse) have never actually tried
implementing an IO sub-system from the ground up, starting with
nothing but bare hardware.
> I think such decisions are just generally poor design, and
> it should not be done in any language. The number of physical
> resources that a program can control should never be assumed
> to be one; things change.
So is it reasonable to assume that there is only one OS?
Perhaps it would be best to let individual Haskell users decide
for themselves what assumptions are or are not reasonable in the
context of their work.
More information about the Haskell-Cafe