Imperative Notation

Nick Kallen nkallen@uclink4.berkeley.edu
Tue, 9 Feb 1999 13:57:46 -0800


> I don't agree. To me a few extra references to the environment are a low
> price to pay, if they save me
> misunderstandings and long sessions of bug-hunting later. *That*
> is a pain I
> almost forgot existed
> as they still do in the imperative world.

Not to sound like a Richard A. O'keefe, but--no.

I don't want to sound polemical, but either you are the weirdest programmer
on earth, Mr. Zuurbier, or you're just mistaken. Or, of course, I'm wrong,
but this is not a point worth considering ;).

A sequence of operations in which there is obviously a state is in no way
simplified by explicitly passing the state around. What bugs you could
possibly avoid by realizing the you're passing the world around from
function to function I find impossible to fathom.

Programming an interface is still easier in Java than Clean (and Haskell),
despite the Object IO library. It could be argued that it's my relative
inexperience with the IO library, but I think it's something much deeper:
Object IO is harder.

I haven't updated the Clean repository in a while, but it may be worth
mentioning that I tried to port Erik Meijer's CGI library to Clean. You know
what I discovered? It's hard as hell. In Haskell, everything is a trivial do
operation. The IO looks like it should look, i.e., an imperative sequence of
events. The Clean on the other hand is a mess of opening and closing
environments, functions with silly types, verbose # statements, and so on ad
nauseum.

Amazingly enough, I was practically copying code from Erik Meijer's library,
but debugging was still a pain! Why? Because the actual code size was almost
twice as big. Try glancing through a long sequence of imperatives with the
explicit CPS. It's impossible. Clean's IO library (0.8) at the time was
somewhat annoying as well, due to imo the lack of a standard
interface--whereas in the Haskell world the monad is the interface so it is
completely standard.

While we're telling people what to do, go write a CGI program in Perl and in
Clean and compare the two. The Perl is easier to write. What? How is it
possible? Well, as great as Clean is for doing all sorts of stuff, including
string processing, doing the actual IO is far more effort than its worth.
Let me tell you, I *hate* Perl, it's a disgusting language. But what's a CGI
programmer to do? I need to be efficient: I'm usually paid per project, not
hourly.

Now, to further illustrate my antithetical-to-Zuurbier point, consider the
parser library. The implicit passing around of the "what's not been parsed"
is what makes the use of high-level combinators possible. The fact that at
the end of the paper there's a translation to monads is no surprise: hiding
variables (and other things) is one of the most fundamental principles of
computer science: abstraction; reduce complexity!

Now, at this point, much of the above is futile assertion. Mr. Zuurbier, I
suggest you do some more IO stuff. Try to do some complicated file
manipulation in Clean. Write a short video game in Clean. Now do the same in
Perl and Java, respectively. You're going to find that it's easier to debug
the imperative code.

I for one am sick of hearing that IO is not really the domain of functional
languages. I can do IO really well in Scheme. Now the solution might not be
as clear in Clean, but it *has* to be there, otherwise this goal of a
real-world general purpose language will remain elusive. Why use "general"
when you actually mean "most stuff, just not really IO." Sure Clean can do
IO, but C++ can do closures too ;) (c.f. comp.lang.functional 2 weeks ago).

I'm going to sketch some really informal stuff that will hopefully spur some
more concrete discussion and debate. (I'm going to completely ignore the
structure of the IO libraries and just try to do this as simply and as
clearly as possible.)

Consider the task of reading two files from two different computers (thus
two different files systems), writing an operation on the file, and storing
the file.

Since we're doing the same operation on both files, we might as well write
an imperative for it.

chug :: IO ()
chug = do
    myFile = openFile "myFile"
    newFile = f myFile
    writeFile "newFile" newFile

computer1, computer2 :: Computer

Start :: [IO ()]
Start = do
	map (chug o openFileSystem) [computer1, computer2]

OK, well the above is IMO the clearest way to chug two computers in
parallel. That's one way to handle multiple environments, but notice these
environments are of the same type. I believe it's very obvious how to map
the above to explicit CPS.

The more complicated situation is the following:
chug a file and animate something on the screen in parallel. But do it
twice. I believe that a solution to this problem is a solution to the
general problem. If I'm incorrect, someone please point it out to me.

myAnimation :: Animation // :: Time -> Picture

Start = do
	(chug o openFileSystem) | ((animate myAnimation) o openMonitor) // [1]

OK, that's once. The | means in parallel. This is also easy to map the
explicit CPS. openFileSystem and openMonitor better not return the same
environment.

Anyway, we're not done, the complication comes from doing this twice. How do
we know what environment we're dealing with after [1]? Well, either the
Compiler can figure it out using the inferred types, or you can simply say
the next statement has to be use the environment of the previous statement
(in this case World as opposed to FileSystem and Monitor).

You can't just do:

Start = do
	(chug o chug o openFileSystem)
	| ((animate myAnimation) o (animate myAnimation) o openMonitor)

But you can do:

Start = do
	chugs | animates
where
chugs = do
	openFileSystem
	chug
	chug
animates = do
	openMonitor
	animate myAnimation
	animate myAnimation

chugs and animates work on single environments, so things can be "done" like
this. I believe that the above maps easily to the explicit CPS. The
programmer, however, has to be aware that he can't do things like:

Start = do
	chugs | chugs

Because they both open the same environment in parallel.

Now, I want to conclude this email with the following remarks. My proposal
is at best a sketch, is completely informal, and more than likely is
insufficient. I am aware of this, but my point in doing it is that I think
it is possible for a reasonable imperative notation to be created. Also, I
apologize for being rude an making fun of people in this email.