[Haskell] Real life examples

John Meacham john at repetae.net
Wed Nov 24 19:28:27 EST 2004

On Wed, Nov 24, 2004 at 11:38:42PM +0000, Ben Rudiak-Gould wrote:
> John Meacham wrote:
> >With my mdo proposal, and I think all proposals brought forth, the
> >module system behaves identically to how it normally does for
> >namespace control. [...] modules do not change code at all, they
> >are pure syntantic sugar for deciding what names you can see.
> I'm not sure I understand your mdo proposal correctly then. A lot of 
> this debate has been over what should happen when a module has a 
> top-level action like
>    x <- putStrLn "hello"
> Everyone agrees that "hello" should be printed at most once, and that if 
> the value of x is ever demanded, it should be printed exactly once. But 
> there's disagreement on everything else. What if I import the module 
> containing the above declaration, but it can be proven statically that 
> the value of x will never be demanded? What if it can't be proven 
> statically, but it happens to be true on a particular run? If "hello" is 
> printed even when x's value is not demanded, then import does more than 
> bring names into scope: it also sometimes adds things to the top-level 
> mdo. If "hello" is printed only when x's value is demanded, then import 
> is okay but the <- construct is unsafe (though safer than unsafePerformIO).

There is some debate about this, and I think this is part of why there
is so much confusion about what top-level-initializers are.  

First of all, I should say that I belive that stylistically 
x <- putStrLn "Hello" 
is a bad idea. it is just bad form to put observable actions in
initializers IMHO, However, this does not necessarily mean it should be
disallowed, because technically, there is nothing wrong with it. I think
this causes problems because some people would like haskell to enforce
good style, while at the moment I think it would be best to worry about
technical problems. style can be enforced by libraries and command line
switches to turn on-off features.

'unsafePerformIO' and unboxed types are not exported to the user by
default because they probably don't need them and their use is in some
sense bad styles. this doesn't mean they should be elided from the
language, in fact, they are vital to many of the libraries internals and
being able to write your own libraries as fast as system libraries is a
necessary feature in any modern language.

Now, my mdo proposal as written would have "hello" outputed exactly once
at module start up time no matter what, whether x is demanded or not. it
is equivalant to a program transformation that collects all the top
level initializers and declarations, puts them all in a mdo block and
runs it with the semantics explained in the fixIO paper. (with a
deterministic, but partially undefined order) 

an argument can be made for changing it such that the IO action is only
exectuted if its value is ever demanded, emulating the current
unsafePerformIO hack. this is strictly less powerful than the above
semanics, but some people like it better because in some sense the
activity is not hidden and it preserves the non-trivial property that
importing a module cannot change the programs behavior. (conversly, it
does not allow imported modules to change programs behavior, which might
be a useful feature for a special purpose debugging library which needs
to do some setup for example)

Personally, it does not matter too much which semantics are taken, as
all my examples would work the same either way as will what I expect
99.99% of the uses of this feature. My guess is that it will come down
to what is easier to implement for the compiler. the lazy IO has a lot
of the same problems as the unsafePerformIO hack, but it at least
offloads them to the compiler internals which can hopefully deal with
them better. I think the mdo proposal would be ultimatly cleaner for a
compiler writer and its semantics are better defined. the order actions
are run while partially undefined is fixed at compile time, as opposed
to depending on evaluation order. Also, the mdo way has the advantage of
a known valid and implementable semantics rather than just the 'promise
of the compiler' it won't do unsafe transformations in the lazy IO case. 

> This kind of thing turned a lot of people off to the idea of top-level 
> initialization actions. George Russell's proposal is appealing because 
> it neatly avoids such problems.

With one problem. His proposal cannot be implemented (efficiently)
without top-level initialization actions. It is a potential user of
TLIs, and very well might be the prefered interface for some but it is
not a replacement. 

Just declaring it is done in the library by the system doesn't
really avoid the issue when we are the ones writing the library and
compiler too :)


John Meacham - ⑆repetae.net⑆john⑈ 

More information about the Haskell mailing list