Holy cow! It's been a long time since I've had my mailbox packed with
letters from the Haskell list. Just when I was beginning to wonder if there
were still other people writing Haskell programs... :)
> | First, Simon, I think you're a little biased on this issue. I'm sure that
> | making argv a global constant would be a practical benefit for programs
> | like GHC.
> You're probably right. I am certainly biased towards large programs. But I
> really want Haskell to scale to large programs, rather than become
> unreasonably inconvenient to use when the program becomes big.
Well, I don't want to make a federal case out of this issue, but I what I
had in mind when I made the above statement was the fact that so many "real"
applications these days use a GUI as a front-end. These kinds of programs
typically *don't* accept a large number of command line arguments; instead
they read in parameters from an initialization file. I would almost go so far
as to call the CLI (command line interace) archaic, except that it resembles a
function call so much. :) It's still a fact, though, that even in FL
programming we avoid functions with a very large number of arguments, and
monads are one convenient way around this.
I'm not contesting the idea that the way argv is handled in Haskell
currently is inadequate. But I am saying that the simple solution of making
argv a global constant is not good FL style, for reasons I and others have
already mentioned. When you write a function that takes a large number of
arguments, do you usually solve it by making all the arguments global?? In
Haskell, I think, the preferred approach is to break the function into a
small set of combinators which can be composed instead.
In a sense, this is a moot point here because conventional OSes impose this
kind of CLI "bottleneck", and it only goes to show how out of place FL
languages are in such a context.
> | nooks and crannies of a program. Most applications are not nearly so |
> configurable,
> I disagree! Most "real" programs (i.e. products) are highly configurable.
Well, re: configurability, I still think a compiler is in another dimension
altogether, but even for other highly configurable applications, it is now
considered questionable practice to parametrize them at the command-line for
several reasons:
1. it's ugly and difficult to understand for beginners
2. it encourages the birth of many, unnecessarily dissimilar throw-away
languages with questionable syntax (consider sed, tr, grep, etc.)
3. it is often insufficiently expressive (see #2)
4. it fixes statically
Hence, it is usually much better in "real" applications to provide a way to
set parameters internally, for example by means of an "Options" menu, etc.
[Note: Many of you will insist that programs like sed and tr are so useful
precisely because of the flexibility of their CLI. I don't disagree with
that; I'm just saying that there is some "unnecessary diversity" -- to quote
the Haskell Report.]
> | Third, I could say that choosing to use a functional language is in some
> | sense also accepting the fact that you are "forced to accept some benefits"
> | which you could do without.
> I don't agree. In exchange for the costs of higher-order functions, or
> laziness, or the lack of side effects, I get some benefits. In exchange for
> making argv accessible only via an IO action I get nothing at all.
But you do, and you've admitted it yourself! You get more modularity,
flexibility and maintainability, like for example the ability to parametrize
your program with several sources of input, or input from a file rather than
the command line.
Besides, the point here is not whether we perform an IO action or not. In
fact, I would argue that argv has to do with IO indirectly, in that it
provides string values that can be mapped to input files (for instance). The
point is, for me at least, the scoping. By this reasoning, we might just as
well revert to the "main :: [String] -> String" model, though, which I think
is inadequate.
Since I've gone this far, I might as well explain why I find the whole
global argv idea so outright distasteful. Simon keeps talking about Real
World Applications, so I might as well lay it on the table here and now.
Frankly, I don't give a damn about GUI interfaces, options menus or argument
plumbing. My gut reaction to global argv is that it is bad because it
encourages writing monolithic applications, rather than communicating
processes. The current trend in the Real World now is component-oriented:
things like OpenDoc, ActiveX and CORBA, where programs are small and
dynamically configurable. In this sense, the old, honored Unix tradition of
combinator-like, map- and filter-like programs is still alive and well, and
in some ways Haskell is in a great position to capitalize on it.
Making it easy to read and farm out arguments from a command line packed
with unrelated options is as sure a ticket to the Stone Age as passing out
free copies of a COBOL compiler. It encourages large, monolithic,
non-interactive, stand-alone applications with a dissimilar and proprietary
input syntax; almost exactly the opposite of what we like to see *inside* a
functional program.
> Lennart writes, of Frank's idea
This is in regards to performOnceBeforeMain. For the sake of giving credit
where credit is due, I should note that Sverker Nilsson originated this idea.
Actually, I was going to suggest something similar along the lines of linear
types, but I'm not sure what the semantic restrictions need to be either, and
would be interested to hear suggestions.
I agree that the argv problem is a manifestation of a more general issue,
and that performOnceBeforeMain is kind of a hack.
Claus Reinke mentioned the idea of using first-class modules here:
> So we seem to need a way to *parameterize a collection of declarations* with
> a *runtime* value. This is why simple parameterized modules would not help
> and, if I see this correctly, why even a separate module language ala SML
> extended with higher-order functors would not be enough here - they
> explicitly address modules *before* runtime.
> But with true first class modules, we could do something like this:
> MyCompiler argv =
> struct
> parse = ..... argv ...,
> typeIt = .. f optimize inline,
> ...
> f opt inl = ... argv ...,
> ...
> compile = parse >> typeIt >> ...
> end
>
> main = getArgv >>= \argv->
> checkArgvForErrors argv >>
> (MyCompiler argv).compile
I was going to suggest this as well (and I think it's an excellent
solution), but since first-class modules are not in Haskell (yet) it seemed
premature, and anyways in this case it feels like we're using a sledgehammer
to kill an ant. To take a broader perspective, though, I think first-class
modules are the Right Way to make Haskell fit more readily into the new
component-based model I mentioned briefly above, and if we *did* have them, I
certainly wouldn't object to their use to solve the argv problem.
[Simon again:]
> When I need that benefit, then I'm willing to pay the cost. What bugs me is
> paying the cost simply to be true to the spirit of FP without getting any
> benefit. Maybe I'm just getting old an cynical. But I still believe in
> lazy evaluation so you can't dispose of me altogether. :-)
Phew! I was started to get worried... :)
--
Frank Christoph Next Solution Co. Tel: 0424-98-1811
[EMAIL PROTECTED] Fax: 0424-98-1500