I/O

Jack Waugh — 2001-06-19 10:32:23

In this message, I will write feelings. I distingush feelings from
thoughts with the sense that thoughts are articulations I could argue
for. In the case of feelings, I want to assert some statements but I
cannot yet articuate an argument for them.

I feel:

The open files in the argument and result in Joy present more of a
challenge to Joy's claim to be a purely functional language than
would a dictionary, if we added one to the argument and to the
result. Von Thun's line of discussion seems to be leading toward
saying that including dictionaries in the argument and result would
smack of assignment, so would break pure functionalism. But he also
says the property "pure functional" is not a matter of degree, so
the "smack of" doesn't seem to fit.

Haskell is a pure functional langauge. It exhibits referential
transparency when read it at face value (Joy seems to have two very
distinct face values, but Haskell has only one because it looks much
like ordinary mathematical notation).

Erlang is an almost-functional language. If you define a function
that doesn't do any I/O, it is functional. But the I/O in Erlang
does not preserve referential transparency. That in Haskell does.
Big difference.

What about Joy?

If we read Joy words as functions in one parameter with one result,
where the data type of the parameter is the same as that of the
result, and where an instance of that data type contains a stack and
some open files, then under this reading, Joy is purely functional
(so is C under the same reading). Under this reading, the files do
not break referential transparency because the argument is linear.
That means you cannot reuse it. Applying a function to the argument
eats up the argument forever. (Some languages allow linearity as an
optional feature of data types.) By including files, the argument
effectively includes the whole world outside the computer. Let's say
I scratch my elbow while "dup" is being executed. dup has been
applied to an argument that included my itchy elbow. The result
includes a less-itchy elbow. This does not break referential
transparency (RT) becaue dup will never again be applied to exactly
the same argument.

There is another way to read Joy and I think that if Joy enters
practical use, this is how programmers will usually read it. Let's
take "+" for example. Instead of reading + as a function in one
parameter and producing one result, as I described above, we can read
+ as a function taking two arguments and producing one result. The
arguments are on the stack before + executes. It consumes the
arguments from the stack and pushes the result. Under this reading,
there is a stack and it changes. We have said in other messages that
this reading is false. But the truth is that Joy is an independent
object of study and this analysis is as valid as the other one. It
will predict the behavior of a system just as well. Now, under this
reading, where we talk about the values being popped from the stack
as arguments to functions and the result pushed as the result from
applying the function, Joy is still almost functional. In
particular, under this reading, there are no variables. In this
reading, von Thun is right to attribute importance to the absence of
variables. Suppose we could have a variable x, and some notation,
say "x?", that would mean push the current value of x onto the
stack. Then x? would not be RT, because its results as pushed on the
stack would not depend solely on its arguments as popped from the
stack. (Under the other reading, x? is RT because it just maps from
an argument containing a dictionary, a stack, and an I/O world to a
result containing a dictionary and a stack and an I/O world.) Joy
doesn't have variables, so no variables break RT when we read Joy
under the interpretation that functions take arguments from the stack
and push results on the stack.

However, under the same reading, Joy is not purely functional for the
same reason that Erlang is not. That is that the I/O world is
implicit. It doesn't enter and leave functions via the official path
of this reading, being popped from and pushed on the stack.

So I'm left with one reading of Joy where it is purely functional but
only in the trivial way that we can read C as purely functional, and
a second reading of Joy where it is not purely functional for the
same reason that Erlang is not. And all this is because of Joy's I/O
model.