Sun, 16 Feb 2003 10:40:05 -0800 (PST)
Jon Cast writes
>I, personally, haven't written a program whose bulk will fit in a single
>file in several years, and I doubt I ever will again. So, support for
>separate compilation is a necessity. How do you intend to handle this?
Hmm; good point.
I see I have been guilty of a careless, uncharitable reading of Hughes's
the first three pages of Hughes's paper refer to "module" and the
equivalent word "package", but I skipped over those references on first
Moreover, when at the top of page 4 Hughes writes, "but such a
variable is bound by a \-expression, and not at top level," I missed the
relevance of the fact that only top-level binding are eligible for
export from a module.
So, my previous post was unfair to Hughes because Hughes's four
solutions to the "global-mutables" problem seem to be able to span
module boundaries, whereas my solution does not.
Specifically, my "lexical-scope" solution allocates a global variable
via the code fragment
>newIORef 0 >>= \globalVariable-> lala
but there is no way in Haskell to export a lambda-bound variable, which
is what globalVariable is.
There's more bad news for my solution.
It is a very common pattern in the imperative world for a module to
contain a piece of "hidden" (not exported) state. In fact, this is
one of the defining characteristics of the OOP paradigm.
And the addG, removeG, frontG, isEmptyG that figure so large in Hughes's
paper constitute and interface to a piece of hidden state, so it has
module-like characteristics, even though Hughes does not identify
it as a module.
If we try to use my lexical-scope solution to implement this very common
pattern, as follows, we run into trouble if we try to put it in its own
module because the variables addG, removeG, frontG and isEmptyG are not
bound at the top level and thus not eligible for export.
> ioref<-newIORef ...
> addG = ...
> removeG = ...
> frontG = ...
> isEmptyG = ...
> --code that refers to addG, removeG, etc, goes here.
In summary, my lexical-scope solution works only when
the solution does not need to cross module boundaries.
We could change Haskell's module system so that it
allows the export of lambda-bound and let-bound variables.
Or we could use the implicit-variables solution reccommended
in Hughes's paper.
I'm not going to try to make a decisive argument for either one, but in
the rest of this post I want to put in a few good words for the
alternative of changing Haskell's module system.
What incited me to write the rest of this post is not module systems per
se. Rather, I wish to use modules systems and Hughes's global-variables
paper as examples with which to express my scepticism or concern over
the design decisions behind extensions to Haskell whose purpose is to
support imperative programming.
The rest of this post (61 lines long) is not terribly important or
penetrating, so stop reading now if you value your time! Maybe I should
have just deleted it rather than posting.
Haskell's module system was probably not designed with imperative
programming in mind.
In other words, it was designed before we had ambitions for Haskell to
be an imperative programing language as well as a declarative
programming language. (Of course, not everyone in the Haskell community
has such ambitions.)
maybe it is time to re-examining the design decisions behind the module
The imperative world has 27 years of experience with module systems,
starting with Modula in 1975. It is likely that some of the design
decisions that have stood the test of time in the imperative world can
be applied directly to Haskell.
We probably do not want slavishly to copy into Haskell a module system
from the imperative world because no imperative language has Haskell's
type classes, nor AFAIK its algebraic datatypes and pattern matching.
Both of these features probably overlap in functionality
with module systems popular in the imperative world.
A good language designer will want to understand the areas of overlap
before designing a new module system for Haskell.
There is an analog in the imperative world to Haskell's notion of
a variable that receives the "result" of a monadic computation:
it is the assignment statement.
If the module systems that have withstood the test of time in the
imperative world can export entities that have been give a value by an
assignment statement, I am perfectly happy for Haskell's module system
to change so that it can do the same thing.
I see nothing wrong with looking toward the imperative world for ways of
implementing intrinsically imperative things (like global mutables).
And if the result of importing (pun not intended) from the imperative
all have types in the IO monad, that's okay with me: I see nothing toxic
or evil about the IO monad.
One might raise the objection that if Haskell simply copies the design
decisions of the imperative world, then there is no reason for Haskell
to exist: we might just as well use an imperative language instead.
Two responses to that:
(1) for the programmer to be able to switch gracefully between
imperative and declarative programming is not an ability offered
by imperative languages.
(2) even if one abandons declarative programming altogether, the
idea of doing imperative programming in a referentially-transparent
way, with every side-effect wrapped in a monad, is by itself exciting
Finally, as an appendix, I collect previous threads on this
subject and the related subject of reading a configuration file
or getting the commandline arguments at the start of one's program.