Hudak on robotic simulation

[from Tracy B. Harms (2008-10-08 13:39 Pacific)]

The following presentation caught my attention, and may be of interest
to others in CSGnet. It involves robotics programming, particularly
as approached through Yale University's Autonomous Systems course.

Arrows, FRP, and Functional Reactive Programming
by Paul Hudak
http://people.cs.uu.nl/johanj/afp/afp4/hudak.ppt

Page 47 makes explicit reference to control theory.

Page 16 turns attention toward the sort of programming I've been
studying recently.

There are some serious shortcomings here along the lines of close
coupling between the perfect knowledge of the modelled systems and the
input provided to those systems. Still, there's plenty here for me to
get value from studying.

Tracy Harms

[From Bill Powers (2008.10.09.0340 MDT)]

Tracy B. Harms (2008-10-08 13:39 Pacific) --

The following presentation caught my attention, and may be of interest
to others in CSGnet. It involves robotics programming, particularly
as approached through Yale University's Autonomous Systems course.

Arrows, FRP, and Functional Reactive Programming
by Paul Hudak
http://people.cs.uu.nl/johanj/afp/afp4/hudak.ppt

Page 47 makes explicit reference to control theory.

Page 16 turns attention toward the sort of programming I've been
studying recently.

I'm afraid I haven't the faintest idea what all this is about. It all seems like a horribly complicated way to talk about something very simple, but since I don't grasp any of it, it's possible that this is very sophisticated stuff beyond my comprehension. If the latter, I congratulate you on being able to think at this level of abstraction. I hope you will interpret it for me one day.

Best,

Bill P.

[From Bill Powers (2008.10.09.0400 MDT)]

Tracy B. Harms (2008-10-08 13:39 Pacific) –

I’ve looked up a little more on “reactive programming,” and am
getting a sort of incredulous glimmering of what they’re trying to do. I
call it “simulation,” only they’re trying to do it as if
they’ve reinvented the entire wheel without realizing that there are far
easier ways of doing the same things.

Check this out:


http://www.cs.nott.ac.uk/~nhn/FoPAD2007/Talks/nhn-FoPAD2007.pdf

Here the “key concept” is said to be “Functions on
signals,” diagrammed like this:

182e956d.jpg

This leads to the following:

182e95ea.jpg

So the output of one box is the input to the next, each of which converts
its input variable into an output variable. Haven’t you seen something
like that before? Like in the PCT diagram? Only what the heck is that
wierd notation at the bottom for? Isn’t the diagram clearer than its
“functional” representation?

I find this in the Widkipedia on Functional Reactive
Programming:

···

=============================================================================

The key points of FRP
are
[2]
:

  • Input is viewed as a “behavior”, or time-varying stream of
    events
  • Continuous, time-varying values
  • Time-ordered sequences of discrete events

According to

Peter van Roy
:

Functional Reactive Programming like functional programming except
that each function can accept a
stream
of input values. If a new input value arrives on an argument, then the
function is reevaluated with all the most recent input values and a new
output value is created. This is a kind of
dataflow
programming
. It can handle nondeterminism, so it is more expressive
than declarative concurrency. In fact, it can be implemented with the
declarative concurrent model extended with WaitTwo. (This means that it
is similar in expressiveness to concurrent logic programming!)

I get a picture of a man from Alpha Centauri visiting the ruins of Earth,
picking up a pencil, and trying to reason out from some alien point of
view what this assembly of cellulose and graphite could possibly be used
for. I think these guys are discovering what we call simulation or
modeling, only they don’t have any idea that it’s all been done before,
many decades ago, and isn’t anywhere near as complex as their way of
doing it and thinking about it.

Help, somebody! Either I am stupendously ignorant and have lost some
whole level of neurons, or this is a wierd example of isolated
specialists tangling themselves up with exaggerated and unnecessary
complexities – lost in the forest and trying to find some
trees.

Best,

Bill P.

[from Tracy B. Harms (2008-10-09 11:03 Pacific)]

Hi Bill,

I'll do what I can to clarify things. On some aspects of this I'm
definitely a beginner, not an expert. By now you've seen that this is
closely related to PCT, as we'd expect from any project where computer
programs are used to simulate robots.

Functional programming differs from imperative (traditional)
programming by expressing things in mathematical terms rather than in
"mechanical" terms. Functional programs describe the required change
(from input to output) as a function defined in terms of component
functions. This eliminates reference to states, which has several
advantages. ("State" here means the implementation that occurs in the
computer as calculation occurs.) A classic example is
"somany:=somany+thisquantity" inside a loop to build a summation of
the individually referenced quantities. In such code the meaning of
"somany" depends on the state of the program at each point where that
line of code is executed. Functional programming abstracts above such
things.

My attention was drawn to these robotics-simulation projects because
they use "arrows", which I just happen to be researching. That topic
is an advanced topic within functional programming, which itself is a
pretty difficult field of study. To summarize what's going on with
"arrows", such programming specifies only the relationships between
functions, and not any of the data that will be fed to those
functions. In Functional Reactive Programming the data being hidden
are the signals received by each component. Such a program specifies
relationships between signal-processors without exposing how the
signals themselves are represented.

The advantage of doing that is that you can simply specify what a
system should do, and the result is a program that actually works as
specified. It's like writing a design document that serves as
executable code.

Now I think I'm ready to turn to your question:

Only what the heck is that wierd notation at the bottom for? Isn't the
diagram clearer than its "functional" representation?

I agree that the diagram is clearer in various ways, but the
functional representation has several advantages. Most important of
these is that you can type it into a computer and get a working system
that has the diagrammed properties. There are other advantages,
though, which become more clear as one becomes able to think about
systems in terms of such notation. Compactness, for example.
(Diagrams involve tedious difficulties in arranging and rearranging
boxes and connections as components become numerous and complex.)

I intend to produce control-systems programs using techniques that are
similar to what you've been looking at here. I'll try to keep any
questions that come up constrained to the control-theory side of
things, rather than have programming issues cause further distraction
here.

Tracy

···

On Thu, Oct 9, 2008 at 3:31 AM, Bill Powers <powers_w@frontier.net> wrote:

[From Bill Powers (2008.10.09.0400 MDT)]

Tracy B. Harms (2008-10-08 13:39 Pacific) --

I've looked up a little more on "reactive programming," and am getting a
sort of incredulous glimmering of what they're trying to do. I call it
"simulation," only they're trying to do it as if they've reinvented the
entire wheel without realizing that there are far easier ways of doing the
same things.

Check this out:

http://www.cs.nott.ac.uk/~nhn/FoPAD2007/Talks/nhn-FoPAD2007.pdf

Here the "key concept" is said to be "Functions on signals," diagrammed like
this:

This leads to the following:

So the output of one box is the input to the next, each of which converts
its input variable into an output variable. Haven't you seen something like
that before? Like in the PCT diagram? Only what the heck is that wierd
notation at the bottom for? Isn't the diagram clearer than its "functional"
representation?

I find this in the Widkipedia on Functional Reactive Programming:

=============================================================================
The key points of FRP are [2]:

Input is viewed as a "behavior", or time-varying stream of events
Continuous, time-varying values
Time-ordered sequences of discrete events

According to Peter van Roy:

Functional Reactive Programming like functional programming except that each
function can accept a stream of input values. If a new input value arrives
on an argument, then the function is reevaluated with all the most recent
input values and a new output value is created. This is a kind of dataflow
programming. It can handle nondeterminism, so it is more expressive than
declarative concurrency. In fact, it can be implemented with the declarative
concurrent model extended with WaitTwo. (This means that it is similar in
expressiveness to concurrent logic programming!)

I get a picture of a man from Alpha Centauri visiting the ruins of Earth,
picking up a pencil, and trying to reason out from some alien point of view
what this assembly of cellulose and graphite could possibly be used for. I
think these guys are discovering what we call simulation or modeling, only
they don't have any idea that it's all been done before, many decades ago,
and isn't anywhere near as complex as their way of doing it and thinking
about it.

Help, somebody! Either I am stupendously ignorant and have lost some whole
level of neurons, or this is a wierd example of isolated specialists
tangling themselves up with exaggerated and unnecessary complexities -- lost
in the forest and trying to find some trees.

Best,

Bill P.

[From Bill Powers (2008.10.09.1642 MDT)]

Tracy B. Harms (2008-10-09 11:03 Pacific) –

Functional programming differs
from imperative (traditional)

programming by expressing things in mathematical terms rather than
in

“mechanical” terms. Functional programs describe the required
change

(from input to output) as a function defined in terms of component

functions.

How is that different from my simulation programs?

This eliminates reference
to states, which has several

advantages. (“State” here means the implementation that occurs
in the

computer as calculation occurs.) A classic example is

“somany:=somany+thisquantity” inside a loop to build a
summation of

the individually referenced quantities. In such code the meaning of

“somany” depends on the state of the program at each point
where that

line of code is executed. Functional programming abstracts above
such

things.

I don’t understand how that can be done, unless you just mean
writing

somany := integral(thisquantity).

Any time you write a statement like that, it has to be implemented in
code, and the code would be

somany := somany + thisquantity* dt

where dt is the duration in simulated time of one
iteration.

So is that all that functional programming means? y = f(x)?

In Functional Reactive
Programming the data being hidden

are the signals received by each component. Such a program specifies

relationships between signal-processors without exposing how the

signals themselves are represented.

That’s how my simulations work, isn’t it? I show the signals entering
functions and coming out of them, with the signals being variables of
unspecified behavior. The function boxes define the operation that is
applied to the varying input variable to create the varying value
of the output variable.

The advantage of doing that is
that you can simply specify what a

system should do, and the result is a program that actually works as

specified. It’s like writing a design document that serves as

executable code.

Well, maybe, but someone has to write the actual executable code. Are you
talking about something similar to Vensim (
www.vensim
.com/ )? Vensim
allows you to pick from a menu of predefined operations like integration,
summation, and time-delays represented as blocks of a block diagram, and
by connecting them construct a graphical simulation of a system
that runs. Wolfgang Zocher and I wrote a similar thing, non-graphical,
that allows the use of predefined function blocks to create simulations,
from ASCII strings. But of course we (and the author of Vensim, Bob
Eberlein) had to write the actual code that makes each function block
work. Wolfgang’s and mine was called Simcon (the first version that I
wrote) and then SimPCT (which was Wolfgang’s improvement on it).

Here’s an example of a Simcon program simulating a two-level control
system that controls the position of a mass suspended on a spring:

···

==============================================================================

title Control Of a Mass on a Spring

time 2.5 0.002 # duration of run, duration of one
iteration, in sec

ENVIRONMENT: mass on spring with viscous damping and disturbance

#sum of disturbance force, friction force, spring force & output
force

qd1 generator puls 1.0 1.5 17.0 #force disturbance acting on
mass

vel integrator 0.0 acc 1.0 #
integrate forces to get velocity

pos integrator 0.0 vel 2.0 #
integrate velocity to get position

acc summator qd1 1.0 vel -0.3 pos -1.5 qo1 1.0

FIRST LEVEL OF CONTROL – VELOCITY CONTROL

se1 comparator so2
vel #
first-level loop: velocity feedback

qo1 amplifier se1 0.0 12.0 0.01 # first-level output
function

SECOND LEVEL OF CONTROL – POSITION CONTROL

sr2 generator puls 0.0 2.5 5.0 # reference signal,
position control

sp2 summator pos 10.0 fb2 -1.0 # second-level input
function with

fb2 amplifier sp2 0.0 9.0 0.08 # integral neg fb (phase
lead)

se2 comparator sr2
sp2 #
second-level comparator

so2 amplifier se2 0.0 4.0 0.01 # second level output
function

PROGRAM DIRECTIVES

group qd1 vel pos
acc

minimize environmental delays

group sp2
fb2

fast feedback

print acc vel pos qd1 sp2 sr2

plot 0.1 1.0 1.0 1.0 1.0 1.0

=============================================================================

Of course you need the definitions of the various terms like
“generator” to understand what each function block does. For
the first “generator” function, the output variable (first
named variable on the left) is a square pulse (“puls”) that
starts at time 0.0 seconds, has a duration of 2.5 seconds, and has an
amplitude of 5.0 units.

Is this the sort of thing that “functional reactive
programming” does?

Best,

Bill P.

[from Tracy B. Harms (2008-10-09 22:15 Pacific)]

Bill,

It looks to me like you've put forth questions regarding (1)
functional programming in general, (2) functional reactive programming
in particular, (3) the application of functional techniques to
autonomous system simulations, and (4) computer language theory. For
this forum I think topic #3 is the most germane, but not long ago you
mentioned that programming discussions tend to take place off-list. If
that becomes more appropriate, I'll understand. Also, I'll be neither
surprised nor dismayed if other programmers here do not join me in my
preference for particular languages or techniques. I am excited about
sharing interests in programming PCT systems, regardless.

My knowledge of "functional reactive programming" is so new and so
slight that I cannot bring myself to try to describe it further. I'm
guessing that the Simcon program you provided is *not* the sort of
thing that FRP does, but I say this mainly because the odds seem
against anybody who tries to draw a parallel with so little
familiarity with the target topic.

As for contrasting your simulation programs with functional programs,
I'm willing to approach the task, but not particularly eager. What I
am eager to do is study your programs more and better, and I'm eager
to write similar programs myself. That is, I want the programs to have
in common that they accomplish PCT modeling. At that point we can look
at how my programs differ from yours, if you'd like, but at present
what excites me is the ways in which they will be alike.

Still, I can elaborate on functional programming somewhat here. I
think it is a marvelous topic, actually. Let me start by noting that
the language I know best for such purposes is not so "pure" a
functional programming language as the language involved in the stuff
reviewed in the prior posts. The language in those examples is
Haskell. It looks very impressive, but I can't write in it. The
language I've most recently gained some degree of competence in is J,
which was designed by Ken Iverson as a refinement and extension of his
earlier language, APL. Given how long you've been involved with
computing, Bill, I'm sure you'll recognize APL.

So, one caveat is that the examples I'll be best at giving will
reflect features of J, not necessarily features of functional
programming in general.

You asked "is that all that functional programming means? y = f(x)?"
In general, yes; you're right, that's what it means. However,
programming has been biased heavily in another direction for a long
time. Function-oriented alternatives have been around for a long time,
too, particularly Lisp and APL, but they've rarely been in a spotlight
position. Most programmers have little or no familiarity with the
functional approaches.

Here is an example written in J that gives an initial exposure to
function-level programming:

   avg=: +/ % #

(This has been indented three spaces to indicate that it was entered
in an interactive J command-line console, which indents so as a
prompt.)

This program has been named "avg" because it computes the average, or
arithmetic mean, of its input. It takes as input a regular numeric
array. (Array handling as seen here is not distinctive to functional
programming, but instead is distinctive to the Iverson languages.)

The program specifies that the result is the summation of the items
divided by the number of items. "+/" means summation or total, "%"
means division, and "#" means tally or count.

The program can be used in the console like this:

   avg 4 6 7 9 13
7.8
   avg 3 4 5 6
4.5

I'm going to have to call it quits for tonight, but I think this
program should demonstrate that this approach is very different from
the sort of code you're used to reading and writing. The differences
are dramatic in many ways.

Tracy

[From Bill Powers (2008.10.10.0133 MDT)]

Tracy B. Harms (2008-10-09 22:15 Pacific) --

Bill,

It looks to me like you've put forth questions regarding (1)
functional programming in general, (2) functional reactive programming
in particular, (3) the application of functional techniques to
autonomous system simulations, and (4) computer language theory. For
this forum I think topic #3 is the most germane --

Yes, I agree.

My knowledge of "functional reactive programming" is so new and so
slight that I cannot bring myself to try to describe it further. I'm
guessing that the Simcon program you provided is *not* the sort of
thing that FRP does, but I say this mainly because the odds seem
against anybody who tries to draw a parallel with so little
familiarity with the target topic.

Just looking for a jumping-off point. One thing about Simcon that's not obvious is that you can write the individual lines of the program statements in any sequence (other than some directives). The connections between the lines are given by the input and output variables for each block, and all connections are assumed to be active at the same time (the "group" directive allows treating a sequence of operations in a single iteration when parallel operation isn't important). All inputs to a block are transformed by the function in the block to a "new" value of the output variable. Then, after all "new" outputs have been calculated, they are all copied into "old" values which are used as inputs on the next iteration. Simcon thus emulates parallel processing.

What I am eager to do is study your programs more and better, and I'm eager
to write similar programs myself. That is, I want the programs to have
in common that they accomplish PCT modeling.

That's my bottom line, too. But an important line item is communication. It's possible to program in many different ways, but some ways are easier to learn, and hence to communicate to other people.

The language I've most recently gained some degree of competence in is J,
which was designed by Ken Iverson as a refinement and extension of his
earlier language, APL. Given how long you've been involved with
computing, Bill, I'm sure you'll recognize APL.

Yes, for sure. I found myself totally unable to learn it (or perhaps the word should be "unwilling.") I think it's a short-term memory issue. As I recall, APL (short for "A Programming Language") used 256 single-character symbols which had to be memorized to become fluent in the language. I didn't get into it far enough to learn much more than that, so I never had the breakthough to understanding the language.

So, one caveat is that the examples I'll be best at giving will
reflect features of J, not necessarily features of functional
programming in general.

You asked "is that all that functional programming means? y = f(x)?"
In general, yes; you're right, that's what it means. However,
programming has been biased heavily in another direction for a long
time. Function-oriented alternatives have been around for a long time,
too, particularly Lisp and APL, but they've rarely been in a spotlight
position. Most programmers have little or no familiarity with the
functional approaches.

Here is an example written in J that gives an initial exposure to
function-level programming:

   avg=: +/ % #

(This has been indented three spaces to indicate that it was entered
in an interactive J command-line console, which indents so as a
prompt.)

This program has been named "avg" because it computes the average, or
arithmetic mean, of its input. It takes as input a regular numeric
array. (Array handling as seen here is not distinctive to functional
programming, but instead is distinctive to the Iverson languages.)

The program specifies that the result is the summation of the items
divided by the number of items. "+/" means summation or total, "%"
means division, and "#" means tally or count.

OK, how is that different from
  avg := 0.0
  for i := 1 to number do avg := avg + input[i];
  avg := avg/number;
  textout(10,10,inttostr(avg));
?

Of course the J program language says all this in one line of symbols, but the reader has to imagine the "input" array, initialization of the average, the size of the array (or the counting process that checks the size as you go), and the indexing process. If you don't want to do one of those operations in the way that is standard for J (for example, summing the array from the second to the next-to-last entry, or summing the odd-numbered entries, or skipping the initialization in order to do cumulative averaging), how can you change the way it's done? Or are you stuck with the way the author of the language chose?

The program can be used in the console like this:

   avg 4 6 7 9 13
7.8
   avg 3 4 5 6
4.5

The "Forth" language had a similar sort of user interface (as can any language I know about, if you write it into the program). In Forth you could write

3 4 + .

And see "7' instantly (the . says print it, if I remember right). This used 'Reverse Polish' notation, in which the items were pushed onto a stack and then popped off it in last-to-first order. The + operation, when popped off the stack, popped two more items off the stack, added them, and pushed the sum back onto the stack. The period popped and printed the top value on the stack. Forth was widely known as a "write-only" language: six months after writing a program, even the guy who wrote it couldn't figure out how it worked. I wrote a couple of long programs before discovering that feature.

I prefer Pascal because the variable declarations are simple and logical, the multicharacter symbols remind the reader of what they mean, and the operations are about as close to the machine operations as you need to get. C has its own advantages, including a slight speed advantage, but a lot of it is next to incomprehensible because the rules were never articulated clearly -- never ask a computer-science programmer to write an instruction book.

Any programming language has to be turned into the necessary machine-language codes before the program can run, so there's no computational advantage of any language over any other (unless they introduce a lot of unnecessary machine operations, as in Object-Oriented Programming). The J version of the averaging program, if properly optimized, doesn't result in any difference from my Pascal version above when it comes to the machine operations that are carried out by the computer. And I'll bet that six months after you write a large J program, you will have more trouble figuring out how it works, or teaching it to someone else, than I will have with my simple-minded Pascal program that does exactly the same thing.

I think that programmers, particularly in computer-science departments, tend to forget that all programming languages work exactly the same way when the executable code is finally generated. The source-code language is only a convenience for the programmer (or, for some programmers, a way of showing off one's superior ability to speak so tersely as to be incomprehensible). My bias is toward clarity and simplicity, to make it as easy as possible for others to understand my programs, even if this means more typing. I don't mind typing.

Best,

Bill P.

[Martin Taylor 2008.10.10.10.10]

[From Bill Powers (2008.10.10.0133 MDT)]

I think that programmers, particularly in computer-science departments, tend to forget that all programming languages work exactly the same way when the executable code is finally generated. The source-code language is only a convenience for the programmer (or, for some programmers, a way of showing off one's superior ability to speak so tersely as to be incomprehensible). My bias is toward clarity and simplicity, to make it as easy as possible for others to understand my programs, even if this means more typing. I don't mind typing.

Clarity and simplicity are in the eye of the beholder. Personally, I think the J line of code has far more of both than the four lines of Pascal(?) to compute and output an average, But that's just me.

The bottom line is that all programming languages that operate on Von Neumann computers (the kind we all have) are Turing-equivalent. That is, they all have the same potential abilities. The differences are entirely in how easy it is for a programmer to achieve the desired result. That, in turn, depends on the programmer's background and cognitive style. I had a friend in the late 60's who was of the opinion that everyone should speak APL in everyday commerce, since it was the only completely precise and concise language available (not just on computers). That was his style (incidentally, he demonstrated to me, in 1967, an on-line chat between himself in Copenhagen and a friend in Los Angeles).

I tried using APL for a while, and had Bill's problem of trying to remember what each symbol did. But that's just a question of familiarity, not only with the symbols, but with the manipulations of linear algebra. Chinese people can read and write thousands of meaningful symbols more easily than they write or read alphabetic material. We can't (or most of us on CSGnet can't).

One advantage of some languages, such as J, is that they offer fewer opportunities for silly little mistakes that lead to hours of debugging. Compare:

       avg=: +/ % #

against

avg := 0.0
for i := 1 to number do avg := avg + input[i];
avg := avg/number;
textout(10,10,inttostr(avg));

It's hard to make a typo that isn't immediately obvious in the J line of code, whereas there are lots of opportunities, some of them non-obvious, in the extended version (e.g. in line 2: "for i := 1 to number do avg := avg + input[l];").

I see the difference as being similar to the difference between a GUI and a command-line interface for doing the same task. In the GUI, you have blocks of things that can be done by clicking the appropriate button, whereas in the CLI you have letters that can be put together more fluidly, but you have to know how to do it in precise detail. J is like a compiler for compilers, if you think of these two snippets as being (in J) the user's imagined expression of the problem, the Pascal code as the output of the J-compiler, then assembler code as the output of the Pascal compiler, then machine code as the output of the assembler. You could code the same thing in assembler, or even machine code, if that suits your thinking and background knowledge best.

We had the same issues in my early programming days. I learned in 1954 on a machine with commands like "colon-T-slash-V" (:T/V) which I believe caused a click on the loudspeaker. Then we went to a magical assembler language that allowed us to specify accumulators, index registers, and memory locations in words rather than in arcane symbols. Then we got compilers, such as Fortran. And so we go.

The same arguments apply at every stage. None of these changes made the slightest difference in what the computer could actually do. They affected only the way the programmer tended to think about it, and the likelihood that the programmer could make the machine perform the desired task in a reasonable amount of programming time. Nowadays, hardly any computer user even thinks about the layers and layers of software, microcode, firmware, operating systems ... that help the casual user to, say, write and send a memo (like this one). Users achieve the desired result very easily. Someone could send a message like this using commands like :T/V, but probably not in a lifetime of work.

Languages matter only to the programmer/user, not to the machine. You use the one that lets you most easily and quickly persuade the machine to do what you want. There's no value in saying that you don't think much of this journal article because it's written in Chinese that you don't understand, nor in saying you don't like a program because it's written in an language you haven't learned.

Martin

[from Tracy B. Harms (2008-10-10 11:17 Pacific)]

In reply to Bill Powers (2008.10.10.0133 MDT)

It makes sense that your familiarity with Pascal-style languages will
prevail for you. It's interesting to me that you did do some serious
programming in Forth, as I have a lot of respect for that language.
About three years ago I was trying to decide which programming
language to learn, and it was definitely among the languages I
seriously considered. The idea that it is a "write-only" language does
not fit what I've heard from the experience of programmers who have
relied on it heavily.

There were other comments in your latest post that seem of a similar
tone, and I become a bit heavy-hearted as I read each of them. You
said that "programmers � tend to forget that all programming languages
work exactly the same way when the executable code is finally
generated." This does not match my conversations with programmers at
all. You say this occurs "particularly in computer-science
departments." I don't have hardly any experience engaging with the
people who make up CS departments, but my guess is that they're no
more likely to forget this than other programmers. What I am
interested in about what you said here is not its truth value, though,
it's what must be bothering you so that you say these things.

There is a similar tone to your statement that "The source-code
language is �, for some programmers, a way of showing off one's
superior ability to speak so tersely as to be incomprehensible." It
sounds like you've run into coders who strike you as not merely
wasting their time but also eager to waste your time by getting you to
notice a particularly dense program that you're not about to
understand, and that your sense of it is that they do this in order to
gloat that they're smarter than you are. Geez, that would suck.

Having recently spent so much time and attention learning J, what you
say here really catches my attention. I wouldn't want my effort to
have enlisted me into the sort of club you've pointed to here! How
awful. Yet, the J language is probably the most ripe of any for
receiving this sort of accusation.

J code is naturally terse, even fabulously terse. If the attraction of
terseness was mainly braggadocio, we'd have to envision Iverson's
intent in creating that language similarly. I propose that his
incentive was quite different. I'm guessing that it was rather like my
own excitement about learning his language:

"LET THE MATH SHINE THROUGH."

That idea is the inspiration that is common to J, APL, Lisp, and the
pure functional programming languages such as Haskell and F#. So I
propose. It's also to be found in packages such as Matlab,
Mathematica, and R.

You understand that what a computer ultimately goes through to
calculate a result is pretty damn similar regardless of which
high-level language was used to write source code.
I wholeheartedly agree that "The source-code language is only a
convenience for the programmer." I think we can all agree from the
start that different people will choose different languages in order
to take advantage of different conveniences. What I hope to offer you
(and other readers) is a sense of which particular advantages come
with mathematically-oriented languages.

These languages all have their different approaches, but what they
have in common is making it easy to emphasize meaning over
implementation. I'm interested in what is to be done, not how it is to
be done, except insofar as discussions of "how" favor the highest
level of abstraction that seems appropriate. I admire the way Simcon
moves things to such a higher level, definitely. It does fit this
broad pattern, I see now.

The approach seen in J is heavily notational with a strict, simple
syntax. This does make it harder to learn, I think, and I agree that a
lot of that difficulty ties in with strain on short-term memory. More
than a few times I've wondered if it was prudent for me to have chosen
this particular language, with prolonged struggle as I tried to
approach fluency. If you want you can call me stubborn.

When you looked at the PowerPoint presentation you saw lots of symbols
that made no sense to you, and it had to be rough. But I think amidst
that document you also saw some calculus formulas, and it seems to me
that you did *not* have those formulas in mind when you voiced your
reaction to the thing being so hard to make sense of. Instead, I'm
guessing that those were, in a sense, invisible. That is, you knew
what they meant, and, being familiar with such notation, you had no
alarm at running into some differential equations and the like.

Differential equations require interpretive skill just like
programming languages do. In fact, since computer programs *must* be
formal (or they just won't work), computer languages are necessarily
akin to mathematical formalisms.

The programming vision that I am electrified by is the idea that
computer programs are mathematical objects, and that treating them as
such allows the power of math to be directly available on the
computer. Most importantly, this allows programs to be understood in
the same way that all other mathematical objects are understood.

Later, hopefully tonight, I intend to post a reply to your question
about how your code example is different from mine. I think there are
some interesting specifics to talk about. What I've said here has been
all generalities, I recognize!

Tracy

[From Bill Powers (2008.10.10.1226 MDT)]

Tracy B. Harms (2008-10-10 11:17 Pacific) --

It makes sense that your familiarity with Pascal-style languages will
prevail for you. It's interesting to me that you did do some serious
programming in Forth, as I have a lot of respect for that language.
About three years ago I was trying to decide which programming
language to learn, and it was definitely among the languages I
seriously considered. The idea that it is a "write-only" language does
not fit what I've heard from the experience of programmers who have
relied on it heavily.

I didn't make up the term. But I'll readily admit that the problems I see simply reflect my own limitations as a mathematician or a mnemonmist. If someone can read a page of APL programming as easily as I read a page of Pascal programming, I have no grounds for saying this person should prefer Pascal. Unless, of course, that person is trying to communicate his program to me. And even then, the only grounds for objection are that communicating with me that way is contraindicated if communication is the objective.

I think you probably have that mathematics gene, the same one Richard Kennaway and Martin Taylor have. Mine is bent in the middle somewhere, but it's still more than a lot of people have. I spend a lot of time trying to figure out how to communicate the mathematics of control to people who don't know any math to speak of, and I guess I look for real mathematicians who will do the same for me.

There were other comments in your latest post that seem of a similar
tone, and I become a bit heavy-hearted as I read each of them. You
said that "programmers � tend to forget that all programming languages
work exactly the same way when the executable code is finally
generated." This does not match my conversations with programmers at
all. You say this occurs "particularly in computer-science
departments." I don't have hardly any experience engaging with the
people who make up CS departments, but my guess is that they're no
more likely to forget this than other programmers. What I am
interested in about what you said here is not its truth value, though,
it's what must be bothering you so that you say these things.

I was just doing a bit of overgeneralizing; don't let it worry you. Actually,
Richard Kennaway, who is a real mathematician if I ever met one and in a department of computer science to boot, is very good at communicating what he does and as capable as any engineer of solving practical problems using whatever language the audience prefers.

There is a similar tone to your statement that "The source-code
language is �, for some programmers, a way of showing off one's
superior ability to speak so tersely as to be incomprehensible." It
sounds like you've run into coders who strike you as not merely
wasting their time but also eager to waste your time by getting you to
notice a particularly dense program that you're not about to
understand, and that your sense of it is that they do this in order to
gloat that they're smarter than you are. Geez, that would suck.

Yep, it does. But I no longer run into that since I left academia (where I was a support person, not an academic). Actually the people I was remembering would become very insulted at being called "coders". Coding is for underlings.

Having recently spent so much time and attention learning J, what you
say here really catches my attention. I wouldn't want my effort to
have enlisted me into the sort of club you've pointed to here! How
awful. Yet, the J language is probably the most ripe of any for
receiving this sort of accusation.

Well, I'm sure you will avoid deserving it, while I reserve the right to apply it on a merit basis. What you do in the privacy of your own computer room is none of my business, anyway.

J code is naturally terse, even fabulously terse. If the attraction of
terseness was mainly braggadocio, we'd have to envision Iverson's
intent in creating that language similarly. I propose that his
incentive was quite different. I'm guessing that it was rather like my
own excitement about learning his language:

"LET THE MATH SHINE THROUGH."

That idea is the inspiration that is common to J, APL, Lisp, and the
pure functional programming languages such as Haskell and F#. So I
propose. It's also to be found in packages such as Matlab,
Mathematica, and R.

I've tried several of those including Lisp, and always had the sense of being restricted by someone else's idea of how I should envision the structure of a problem. Maybe that's one reason I'm not a very good mathematician.

You understand that what a computer ultimately goes through to
calculate a result is pretty damn similar regardless of which
high-level language was used to write source code.
I wholeheartedly agree that "The source-code language is only a
convenience for the programmer." I think we can all agree from the
start that different people will choose different languages in order
to take advantage of different conveniences. What I hope to offer you
(and other readers) is a sense of which particular advantages come
with mathematically-oriented languages.

Again, it's a matter of communication. You can't teach music appreciation to a person who is deaf. A "mathematically-oriented language" works best for those who appreciate it. I don't even know what you mean by the term -- I thought I was using mathematics, but evidently I'm not.

These languages all have their different approaches, but what they
have in common is making it easy to emphasize meaning over
implementation.

We would be in complete accord about that, but I fear we don't mean the same thing by "meaning." When I consider a physical situation, I first put the math aside until I understand what's happening. To me, the physical situation is the meaning, while the math is just a stylized (but extremely useful) approximation to a description of it. Again, I admit that this may just be another symptom of my warped math gene.

I'm interested in what is to be done, not how it is to
be done, except insofar as discussions of "how" favor the highest
level of abstraction that seems appropriate. I admire the way Simcon
moves things to such a higher level, definitely. It does fit this
broad pattern, I see now.

This gets confusing. I suspect we're working out of different dictionaries. Maybe the thing to do here is just to stick to the languages our brains are most at ease with, and do our best to make sure the phenomena being modeled are the same. When you get down to using a mouse to control something on a screen, any important differences of meanings in a model quickly become apparent.

The programming vision that I am electrified by is the idea that
computer programs are mathematical objects, and that treating them as
such allows the power of math to be directly available on the
computer. Most importantly, this allows programs to be understood in
the same way that all other mathematical objects are understood.

The last thing in the world I would want would be to cast a pall over anything that "electrifies" you. That's enough to tell you you'e doing the right thing for TBH, and to hell with WTP's gloomy opinions.

Later, hopefully tonight, I intend to post a reply to your question
about how your code example is different from mine. I think there are
some interesting specifics to talk about.

That would be most helpful.

Best,

Bill P.

[from Tracy B. Harms (2008-10-11 09:40 Pacific)]

In reply to Bill Powers (2008.10.10.0133 MDT) reply to my post of
(2008-10-09 22:15 Pacific) --

Hi, Bill. Thanks for your comments on my generalities.

So, lets dive into some details. I posted a program,

avg=: +/ % #

and you asked "OK, how is that different from [this]?"

avg := 0.0
for i := 1 to number do avg := avg + input[i];
avg := avg/number;
textout(10,10,inttostr(avg));

The first point of contrast is between what you named "avg" and what I
named "avg". Yours is a scalar variable, mine is a function. The
meaning of avg, the function, is ratio-of-total-to-tally, which is
explicit in the notation. The program serves as a statement of the
concept.

The meaning of avg, the variable, must be understood by considering
all six places where it occurs. It is used in several ways. Most
prominent are the three assignment statements. Assignments such as
these are not parallel to statements of equality. Plainly avg is not
safely treated as equal to avg + input[i]. Change-over-time is
embedded into the language.

That's quite different from what occurs with copula (=:) in the J
example, where the static nature of things makes it so that avg "is"
what occurs to the right of it. The definition establishes a meaning.
Typical of algebraic expressions, there is no time component here.

So, of course the you could use "avg" to name a subroutine instead, at
which point the parallel would be somewhat better, but that would not
alter the underlying difference I've just described: The Pascal code
specifies changes over time and involves managing intermediate states
and incidental values. The J code specifies a relationship between
input and output as a composition of functions.

You mentioned the basic algebraic relationship, y = f(x) That is a
good map for programs in general (setting aside discussion of
determinacy and stochastic features) so we can think of y as the
output, f as the program, and x as the input data. In the interactive
J console this has the following structure:

   f x
y

Parentheses could be used around the x, but they are normally left out
when adjacency has the same meaning.

Now, I'll define 'numbers' to serve as x and the function 'avg' serves as f:

   numbers=: 2 9 3 7 5 9 0 4
   avg numbers
4.875

The common math notation h(g(f(x))) applies to the following
variation, where we calculate the average where the first and last
elements of the list have been excluded:

   avg }.}: numbers
5.5

You also wondered how you'd calculate the average of every other item
in the list. I'm inclined to define a separate function first, then
apply it:

   oddindices=: #~ 2|i.@:#

   avg oddindices numbers
7.25

(J uses zero-based indexing, so 9 7 9 4 are the values at the odd
indices here.) This newly defined function was again defined strictly
in terms of other functions. What I've used here is a particularly
"hard core" technique in that it does not involve any specification of
parameters, intermediate vales, or output. You made this comment about
avg:

Of course the J program language says all this in one line of symbols, but
the reader has to imagine the "input" array, initialization of the average,
the size of the array (or the counting process that checks the size as you
go), and the indexing process.

Indeed, there is a whole lot for the reader to imagine when it comes
to J. We may even view J as an attempt to minimize specification while
maximizing effect. This does demand a lot of the reader, but it
ultimately lets the reader focus much more exclusively on what
matters. An expression of oddindices in more conventional form would
be:

copy(x, mod(integers(tally(x)),2))

I hope this illuminates how functional programming involves
specification of a program in terms of functions.

You may be unsatisfied by the fact that my solution to the
odd-numbered-item average did not involve modifying the program avg,
but instead giving it as input only the filtered values. Your program,
in contrast, can be easily modified to increment the index by two each
pass instead of one. That's bound to have a performance advantage,
too.

There is something else that deserves to be talked about in
contrasting the two averaging programs we've been looking at. While
I'd only demonstrated the use of (+/%#) on a simple list, in fact it
applies at the outermost dimension of matrices of any rank. Here a
simple (2D) example:

   avg=: +/ % #

   ]mtx=: i. 4 6
0 1 2 3 4 5
6 7 8 9 10 11
12 13 14 15 16 17
18 19 20 21 22 23

   avg mtx
9 10 11 12 13 14

The result is a list wherein each item is the average of the
corresponding column of the input. If the input were, instead, a
four-dimensional map of temperatures across a space over a period of
time, avg would produce a three-dimensional report of average
temperature over time. If you had many such four-dimensional inputs,
you could list them together then apply avg to get a single 4-D
"movie" that is their average.

In each case, no modification is made to the J program. The same
simple program handles all of these. The modifications needed to your
Pascal code would be significant. Indeed, once you've handled the
array management required for such flexibility the obvious thing to do
is hide all that code so that it does not get in the way of
understanding the part that is specific to averaging.

Such setting-aside-of-mechanics is the most important point I hope to
communicate. Whether it's done by packages (such as Simcon), by making
functions first-class objects, or by making array manipulation
implicit, such techniques help programs reflect design so that the
structured design (the "meaning" of the program) is more prominent.

Tracy

[From Bill Powers (2008.10.11.1140 MDT)]

Tracy B. Harms (2008-10-11 09:40 Pacific) --

So, lets dive into some details. I posted a program,

avg=: +/ % #

and you asked "OK, how is that different from [this]?"

avg := 0.0
for i := 1 to number do avg := avg + input[i];
avg := avg/number;
textout(10,10,inttostr(avg));

The first point of contrast is between what you named "avg" and what I
named "avg". Yours is a scalar variable, mine is a function.

OK. I will rewrite my example as a function instead of a fragment of a program. I assume that a variable called "input" has been declared as an open array of integers with a length determined when numbers are loaded into it, the type having been declared as "inputarraytype." The average is returned as a double-precision floating point number:

function avg(input: InputArrayType): double;
var i, sum: integer;
begin
  sum := 0;
  for i := 0 to length(input) - 1 do sum := sum + input[i];
  result := sum/length(input);
end;

Now if another part of the program is to assign the average of the numbers currently stored in the array called "input" to the value of a variable called "v", it executes the statement

v := avg(input);

Assuming that there is a procedure called "print", we could print the average as

print(v),

or if we didn't need to save the average value to avoid recomputing the same value over and over,

print(avg(input));

The meaning of avg, the function, is ratio-of-total-to-tally, which is
explicit in the notation. The program serves as a statement of the
concept.

Perhaps you can see that once a function is defined in Pascal (by the group of statements that starts with the word "function" above), we can then use the function named "avg" exactly as you use it in the J example (though with a different way of writing the statement).

It looks to me as if defining the function 'avg' is exactly the same in Pascal and J except for the notation. Pascal simply makes all the implicit processes in the J statement explicit. Since the steps done automatically in J are spelled out in Pascal, you can change them in Pascal but not in J.

The meaning of avg, the variable, must be understood by considering
all six places where it occurs. It is used in several ways. Most
prominent are the three assignment statements. Assignments such as
these are not parallel to statements of equality. Plainly avg is not
safely treated as equal to avg + input[i]. Change-over-time is
embedded into the language.

Yes. In Pascal, the colon-equal sign means replacement (wqhich you call assignment), not equality in the logical sense. x := x + 1 means that the current value stored in the memory location named x is replaced by the value of the computation on the right: the old value stored in location x with 1 added to it.

That's quite different from what occurs with copula (=:) in the J
example, where the static nature of things makes it so that avg "is"
what occurs to the right of it. The definition establishes a meaning.
Typical of algebraic expressions, there is no time component here.

I don't know which you are referring to by "here." As far as I can see the only difference between the J and Pascal definitions of the function "avg" is what is spelled out in detail and what is simply understood to happen automatically.

So, of course the you could use "avg" to name a subroutine instead, at
which point the parallel would be somewhat better, but that would not
alter the underlying difference I've just described: The Pascal code
specifies changes over time and involves managing intermediate states
and incidental values. The J code specifies a relationship between
input and output as a composition of functions.

I'm not clear what you mean by that. In Pascal, if we define two functions, f1 and f2, we could certainly write

v := f1(f2(argument list 1), argumentlist 2));

where the arguments of f1 are the value of f2 and whatever further arguments, if any, are required for f1.

You mentioned the basic algebraic relationship, y = f(x) That is a
good map for programs in general (setting aside discussion of
determinacy and stochastic features) so we can think of y as the
output, f as the program, and x as the input data. In the interactive
J console this has the following structure:

   f x
y

Parentheses could be used around the x, but they are normally left out
when adjacency has the same meaning.

Also, it looks as if the value of the function is printed out without your asking, when you write just f(x). That's OK, but it's another rule you have to carry in your head.

Now, I'll define 'numbers' to serve as x and the function 'avg' serves as f:

   numbers=: 2 9 3 7 5 9 0 4
   avg numbers
4.875

The common math notation h(g(f(x))) applies to the following
variation, where we calculate the average where the first and last
elements of the list have been excluded:

   avg }.}: numbers
5.5

We could define the Pascal function as

function avg(n1, n2: integer; input: inputarraytype);

where now n1 and n2 are the starting and ending indices of the numbers to be averaged; the "FOR" statement would then be

for i := n1 to n2 do

As far as I can see, there is still no difference other than notation.

You also wondered how you'd calculate the average of every other item
in the list. I'm inclined to define a separate function first, then
apply it:

   oddindices=: #~ 2|i.@:#

   avg oddindices numbers
7.25

(J uses zero-based indexing, so 9 7 9 4 are the values at the odd
indices here.) This newly defined function was again defined strictly
in terms of other functions. What I've used here is a particularly
"hard core" technique in that it does not involve any specification of
parameters, intermediate vales, or output. You made this comment about
avg:

Similarly, in Pascal we could define an oddindices function, or a whole collection of functions to pick out different patterns of entries in the array, and pass the name of the function to the avg function as an argument, or an index number indicating which of an array of functions is to be used.

> Of course the J program language says all this in one line of symbols, but
> the reader has to imagine the "input" array, initialization of the average,
> the size of the array (or the counting process that checks the size as you
> go), and the indexing process.

Indeed, there is a whole lot for the reader to imagine when it comes
to J. We may even view J as an attempt to minimize specification while
maximizing effect. This does demand a lot of the reader, but it
ultimately lets the reader focus much more exclusively on what
matters. An expression of oddindices in more conventional form would
be:

copy(x, mod(integers(tally(x)),2))

I hope this illuminates how functional programming involves
specification of a program in terms of functions.

What I'm getting out of this so far is that what you called "functional programming" is what I call "programming." Perhaps you might give me an example of programming that isn't functional -- that might make what you mean clearer.

You may be unsatisfied by the fact that my solution to the
odd-numbered-item average did not involve modifying the program avg,
but instead giving it as input only the filtered values. Your program,
in contrast, can be easily modified to increment the index by two each
pass instead of one. That's bound to have a performance advantage,
too.

Or I could do it your way by using a separately-defined function. Nothing in Pascal keeps you from doing that. Generally, if a process is used only once you don't write it as a separate function because calling a function involves a considerable speed penalty -- all the registers have to be pushed into a stack, then restored after the call. That's why strict OOP programs are slow; every reference to a variable through a "method" involves all the overhead of a function call.

There is something else that deserves to be talked about in
contrasting the two averaging programs we've been looking at. While
I'd only demonstrated the use of (+/%#) on a simple list, in fact it
applies at the outermost dimension of matrices of any rank. Here a
simple (2D) example:

   avg=: +/ % #

   ]mtx=: i. 4 6
0 1 2 3 4 5
6 7 8 9 10 11
12 13 14 15 16 17
18 19 20 21 22 23

   avg mtx
9 10 11 12 13 14

The result is a list wherein each item is the average of the
corresponding column of the input.

In each case, no modification is made to the J program. The same
simple program handles all of these. The modifications needed to your
Pascal code would be significant. Indeed, once you've handled the
array management required for such flexibility the obvious thing to do
is hide all that code so that it does not get in the way of
understanding the part that is specific to averaging.

Easy to do in Pascal, too, though perhaps more wordy. Input[i,j] refers to the ith column and jth row of a matrix, so a 2-D or n-D matrix of values can be handled quite easily. If you want to leave the dimensions of the matrix unspecified, more labor is required at the level of coding to count the number of entries and adjust the memory allocations at run-time, but once you're written the code to define each such process you don't have to write it again.

All you're talking about here seems to be the degree to which the user of the language leaves the basic underlying operations to the writer of the compiler -- and thereby loses the freedom to invent his own underlying processes. And also has to trust that the other guy did it right.

Such setting-aside-of-mechanics is the most important point I hope to
communicate. Whether it's done by packages (such as Simcon), by making
functions first-class objects, or by making array manipulation
implicit, such techniques help programs reflect design so that the
structured design (the "meaning" of the program) is more prominent.

I don't argue that such techniques aren't useful, expecially to people who don't want to do programming per se but just want a way to generate the results they want. But they require memorizing a lot more arbitrary rules than are needed when you program at a lower level of abstraction, and there's never any guarantee that the person who wrote the lower levels provided the exact operation you want to carry out, or felt any sympathy for your mental limitations.

Rick Marken cheerfully writes simulations in Excel, with variable names like
D7 and Y22 in the formulas, while I, with my leaky memory, lose track immediately of what parts of the physical system they refer to. It wouldn't kill a J programmer if the operation symbols like % were converted to names that give me a chance of working out what they mean, would it? That way I wouldn't have to stop to drag the meanings up out of memory and hope I remembered them right. In fact, I suspect that if you just substituted multicharacter symbols for the single-character ones, and always put in parentheses to remove doubt, and did all the other friendly things that disambiguate and otherwise clarify what is meant, you would end up with program statements that look a lot like Pascal or C, or God forbid, Basic. The actual programming wouldn't look much different.

I'll close by repeating what I said earlier: what you call functional programming looks, so far, like what I call programming -- but without any mnemonic aids at all.

Best,

Bill P.

[From Bill Powers (2008.10.11.1740 MDT)]

Tracy B. Harms (2008-10-11 09:40 Pacific) --

I see I committed some ambiguities that could be confusing.

I used the name "input" in defining the function "avg", and also in calling it. In the function definition, the name in parentheses is only a place-holder, and could be anything; all that is critical is the type of this variable. So I could have written

function avg(v1: InputArrayType): double;
var i, sum: integer;
begin
  sum := 0;
  for i := 0 to length(v1) - 1 do sum := sum + v1[i];
  result := sum/length(v1);
end;

This function can now be called with any variable of the right type (ArrayInputType) as an argument; this variable (an array) will be substituted for "v1" everywhere it appears in the function definition. So what follows is still correct:

Now if another part of the program is to assign the average of the numbers currently stored in the array called "input" to the value of a variable called "v", it executes the statement

v := avg(input);

Here's another one:

In Pascal, if we define two functions, f1 and f2, we could certainly write

v := f1(f2(argument list 1), argumentlist 2));

where the arguments of f1 are the value of f2 and whatever further arguments, if any, are required for f1.

This would have been less confusing if I had numbered the argument lists more appropriately:

v := f1(f2(argument list 2), argumentlist 1));

Best,

Bill P.

[from Tracy B. Harms (2008-10-11 17:25 Pacific)]

re, Bill Powers (2008.10.11.1740 MDT)

...
function avg(v1: InputArrayType): double;
var i, sum: integer;
begin
sum := 0;
for i := 0 to length(v1) - 1 do sum := sum + v1[i];
result := sum/length(v1);
end;

This function can now be called with any variable of the right type
(ArrayInputType) as an argument; this variable (an array) will be
substituted for "v1" everywhere it appears in the function definition. So
what follows is still correct:

Now if another part of the program is to assign the average of the numbers
currently stored in the array called "input" to the value of a variable
called "v", it executes the statement

v := avg(input);

So, given the example array of my last message, mtx

  ]mtx=: i. 4 6
0 1 2 3 4 5
6 7 8 9 10 11
12 13 14 15 16 17
18 19 20 21 22 23

(1) what would be the result of
length(v1)

(2) on the initial pass, when i equals zero, what would be the result of
v1[i]

Thank you,

Tracy

[From Bill Powers (2008.10.l1.2106 MDT);

Tracy B. Harms (2008-10-11 17:25 Pacific)

So, given the example array of my last message, mtx

  ]mtx=: i. 4 6
0 1 2 3 4 5
6 7 8 9 10 11
12 13 14 15 16 17
18 19 20 21 22 23

(1) what would be the result of
length(v1)

(2) on the initial pass, when i equals zero, what would be the result of
v1[i]

As I said in my post, to handle more than a one dimensional array, a lot of background programming would be needed, starting with the process (probably a procedure rather than a function) that reads in the data array, counting the entries in each row and the number of rows (though I see that in J you declare the length of each dimension in advance). Then an open array would be dimensioned at run-time by a statement (in a series of nested loops) like

setlength(v1,n1,n2,...nm);

or in the case above,

setlength(mtx,4,6);

... where v1 or mtx is declared as an open "array of integer" with the size and number of dimensions unspecified. With v1 being multidimensional, the
"length" function would have to be written length(v1[i,j,k] if you wanted the length of the third, kth, dimension. But you already know it, because it was established while counting rows and columns of the input matrix, or was simply specified by the programmer as in your example. In Pascal the array could be loaded by saying

mtx := array[0..3,0..5] = ((0,1,2,3,4,5),(6,7,8,9,10),(11,12,13,14,15),(16,17,18,19,20);

or else by a procedure that reads in the values one at a time from the keyboard in that order.

There would undoubtedly have to be a limit set on the number of dimensions, to keep the total size of the array within the limits of memory. Once the number of and size of dimensions was established the "avg" routine (or a separate initializing procedure) would read the data to determine how many sums and averages had to be computed, and compute accordingly.

I don't know how the programming of an open array works, though I might guess or find out if I thought it important to know.

In J, the writer of the compiler has most probably taken care of all that programming for you, including allocating and releasing memory, so you don't have to know how it works. If you programmed in Pascal, you would have to know. Sometimes I want to know, sometimes I don't care.

One way to program in Pascal (or any similar language) is simply to assume that each function has been defined, and write the top-level program first using the names and argument lists of the functions. That level would look like J programming except for the details of notation. After sketching in the program this way, you would implement each function to make it behave as assumed. Often this results in seeing that it would be better to use different functions, more easily or efficiently programmed, and going back to modify the top-level organization accordingly. This might go on for several additional layers, though for the sake of speed you wouldn't want too many layers. That would make the initial stages of Pascal programming very similar to J programming, except that you could make up low-level functions or procedures in addition to picking from a predefined set. At some level of detail, the two approaches would merge into the assembly-language level, to produce the final executable code in which the operations are determined by the operating system and the architecture of the computer chip.

I use predefined functions at the level of sqrt(x) meaning square root of x, and so on. The Pascal compiler has such functions optimized in assembly language and there's no point in trying to improve on them. I'm sure J works exactly the same way. I write most of my programs by doing the top level first, filling in the functions, going back to the top level, and so on until I settle on a combination I like and that looks organized and readable. That's mostly for my own benefit so I can understand what I wrote a few months later.

Best,

Bill P.

Martin,

Your post made for interesting reading, particularly since your
involvement in programming precedes mine by twenty years. I'm too
young to have known computers in the absence of high-level languages,
and I enjoy thinking about what it must have been like. (I also find
myself savoring the idea that I'm "too young" for something.)

You're absolutely on target that a big part of the familiarity
required for APL (and its successors) is with linear algebra. I think
it is safe to say that the great bulk of my knowledge of linear
algebra is in the form of my J programming skills. Often, while
studying J, I've been unable to distinguish limitations in my
programming skill from limitations in my mathematical understanding.
My successes have naturally magnified my interest in math.

What you said here is something I'd like to endorse and emphasize:

Languages matter only to the programmer/user, not to the machine.
You use the one that lets you most easily and quickly persuade the
machine to do what you want.

The difficulty Bill is experiencing comprehending how "functional"
programming is distinctive persists because his attention turns
consistently to what happens with the machine. But as seems to be
common knowledge, there are no interesting differences at the machine
level.

Tracy

P.S. I'm not enamored with the label "functional programming". I use
it only because it is the most commonly used label, not because I
think it adds clarity to the topic.

···

On Fri, Oct 10, 2008 at 8:08 AM, Martin Taylor <mmt-csg@mmtaylor.net> wrote:

[Martin Taylor 2008.10.10.10.10]

[From Bill Powers (2008.10.10.0133 MDT)]

I think that programmers, particularly in computer-science departments,
tend to forget that all programming languages work exactly the same way when
the executable code is finally generated. The source-code language is only a
convenience for the programmer (or, for some programmers, a way of showing
off one's superior ability to speak so tersely as to be incomprehensible).
My bias is toward clarity and simplicity, to make it as easy as possible for
others to understand my programs, even if this means more typing. I don't
mind typing.

Clarity and simplicity are in the eye of the beholder. Personally, I think
the J line of code has far more of both than the four lines of Pascal(?) to
compute and output an average, But that's just me.

The bottom line is that all programming languages that operate on Von
Neumann computers (the kind we all have) are Turing-equivalent. That is,
they all have the same potential abilities. The differences are entirely in
how easy it is for a programmer to achieve the desired result. That, in
turn, depends on the programmer's background and cognitive style. I had a
friend in the late 60's who was of the opinion that everyone should speak
APL in everyday commerce, since it was the only completely precise and
concise language available (not just on computers). That was his style
(incidentally, he demonstrated to me, in 1967, an on-line chat between
himself in Copenhagen and a friend in Los Angeles).

I tried using APL for a while, and had Bill's problem of trying to remember
what each symbol did. But that's just a question of familiarity, not only
with the symbols, but with the manipulations of linear algebra. Chinese
people can read and write thousands of meaningful symbols more easily than
they write or read alphabetic material. We can't (or most of us on CSGnet
can't).

One advantage of some languages, such as J, is that they offer fewer
opportunities for silly little mistakes that lead to hours of debugging.
Compare:

     avg=: +/ % #

against

avg := 0.0
for i := 1 to number do avg := avg + input[i];
avg := avg/number;
textout(10,10,inttostr(avg));

It's hard to make a typo that isn't immediately obvious in the J line of
code, whereas there are lots of opportunities, some of them non-obvious, in
the extended version (e.g. in line 2: "for i := 1 to number do avg := avg +
input[l];").

I see the difference as being similar to the difference between a GUI and a
command-line interface for doing the same task. In the GUI, you have blocks
of things that can be done by clicking the appropriate button, whereas in
the CLI you have letters that can be put together more fluidly, but you have
to know how to do it in precise detail. J is like a compiler for compilers,
if you think of these two snippets as being (in J) the user's imagined
expression of the problem, the Pascal code as the output of the J-compiler,
then assembler code as the output of the Pascal compiler, then machine code
as the output of the assembler. You could code the same thing in assembler,
or even machine code, if that suits your thinking and background knowledge
best.

We had the same issues in my early programming days. I learned in 1954 on a
machine with commands like "colon-T-slash-V" (:T/V) which I believe caused a
click on the loudspeaker. Then we went to a magical assembler language that
allowed us to specify accumulators, index registers, and memory locations in
words rather than in arcane symbols. Then we got compilers, such as Fortran.
And so we go.

The same arguments apply at every stage. None of these changes made the
slightest difference in what the computer could actually do. They affected
only the way the programmer tended to think about it, and the likelihood
that the programmer could make the machine perform the desired task in a
reasonable amount of programming time. Nowadays, hardly any computer user
even thinks about the layers and layers of software, microcode, firmware,
operating systems ... that help the casual user to, say, write and send a
memo (like this one). Users achieve the desired result very easily. Someone
could send a message like this using commands like :T/V, but probably not in
a lifetime of work.

Languages matter only to the programmer/user, not to the machine. You use
the one that lets you most easily and quickly persuade the machine to do
what you want. There's no value in saying that you don't think much of this
journal article because it's written in Chinese that you don't understand,
nor in saying you don't like a program because it's written in an language
you haven't learned.

Martin

[From Bill Powers (2008.10.13.1502 MDT)]

The difficulty Bill is
experiencing comprehending how “functional”

programming is distinctive persists because his attention turns

consistently to what happens with the machine. But as seems to be

common knowledge, there are no interesting differences at the
machine

level.

It’s not the machine that is giving me trouble understanding the
difference between programming and functional programming. Everything
you’re saying (and what I’m reading) about functional programming sounds
exactly like the kind of programming I’ve always done. The main
difference I see is that in my programming, after I’ve decided on the
functions I will need (for example, an input function, a comparison
function, an output function, and a feedback function) I then have to go
on to write the code for those functions because they aren’t predefined
in Pascal. You’d probably have to write the code for those functions in
J, too, as you did for the “avg” function. You say that the
Simcon program is more like what you call functional programming, but I
had to write the code for interpreting the program statements to make
Simcon work. Somebody has to write the underlying code, even in
J.

You might find the following code pertinent (part of a larger program).
It consists of creation of 5 functions defining matrix operations,
followed by a procedure that uses them to calculate the behavior of a set
of “maxmatrix” control systems, where maxmatrix can be set to
any number – 500 for this instance of the program. There are 500 control
systems each constructing its own perception as a randomly-chosen
weighted sum of all environmental variables, and acting on all 500
environmental variables through another set of weights. The output weight
matrix is the transpose of the input weight matrix for the 500 systems.
The object was to see whether using the transpose of the input matrix in
the output matrix would result in independent control of each
randomly-defined perception (it does).

These functions are declared as procedures because I’m using the
“var” notation in the argument list, indicating that instead of
passing the value of the input matrix or vector to the function, a
pointer to the memory location is passed instead. So if the procedure
changes a variable, it changes it where that variable is stored, rather
than changing it only locally. This takes the place of (and is faster
than) declaring a function and having it return a value that must be
stored somewhere. The notes between curly brackets are comments, not part
of the code.

···

At 11:53 AM 10/13/2008 -0700, Tracy Harms wrote:

============================================================================

{v2 = M*v1 premultiply vector by square matrix}

procedure MV(var M:coefftype; var v1, v2: vectortype);

var i,j: integer;

begin

for i := 1 to maxmatrix do

begin

v2[i] := 0.0;

for j := 1 to maxmatrix do

v2[i] := v2[i] + M[i,j]*v1[j]

end;

end;

procedure VS(var A,B,C: vectortype); { C = A - B Vector subtract}

var i: integer;

begin

for i := 1 to maxmatrix do

C[i] := A[i] - B[i];

end;

{ C = A + B vector add}

procedure VA(var A,B,C: vectortype);

var i: integer;

begin

for i := 1 to maxmatrix do

C[i] := A[i] + B[i];

end;

{B = k*int(A) vector integrate}

procedure VI(var a,b: vectortype; k: double);

var i: integer;

begin

for i := 1 to maxmatrix do

b[i] := b[i] + k*a[i]*dt;

end;

{ CONTROL LOOPS IMPLEMENTED AS (SQUARE) MATRIX OPERATIONS}

procedure controlloops;

begin

MV(inputCoeffs,qi, p); { p = coeffs*qi}

VS(r,p,e);
{ e = r - p}

VI(e,o,ko);
{ o = o + ko * integral(e) * dt}

MV(w,o,temp);
{ temp = w * o}

VA(temp, d,
qi); { qi = temp +
d}

end;

{Note: w and inputCoeffs are 500 x 500 matrices}

==============================================================================

The “controlloops” procedure is iterated indefinitely while the
values of the variables are plotted on the screen. If the matrix
operations had been predefined as part of the language, only the
controlloops procedure would have appeared in the code.

I can send you the whole program if you want to try implementing it in
J.

Best,

Bill P.

[Martin Taylor 2008.10.13.23.45]

[From Bill Powers (2008.10.13.1502 MDT)]

The difficulty Bill is
experiencing comprehending how “functional”

programming is distinctive persists because his attention turns

consistently to what happens with the machine. But as seems to be

common knowledge, there are no interesting differences at the
machine

level.

It’s not the machine that is giving me trouble understanding the
difference between programming and functional programming. Everything
you’re saying (and what I’m reading) about functional programming
sounds
exactly like the kind of programming I’ve always done. The main
difference I see is that in my programming, after I’ve decided on the
functions I will need (for example, an input function, a comparison
function, an output function, and a feedback function) I then have to
go
on to write the code for those functions because they aren’t predefined
in Pascal. You’d probably have to write the code for those functions in
J, too, as you did for the “avg” function. You say that the
Simcon program is more like what you call functional programming, but I
had to write the code for interpreting the program statements to make
Simcon work. Somebody has to write the underlying code, even in
J.

Yes, and somebody had to write the code to turn your Pascal program
into assembler language for your machine, and somebody had to write the
code to turn that assembler language into machine code. That’s the
nature of compilers and assemblers. They help a person with a computing
problem use language nearer to the language in which she thinks. There
are many languages because different kinds of problem domain are more
easily expressed in different kinds of language. I believe that R is a
relative of J, but slanted toward statistical computation. Among
languages of the general class of Pascal, I used to like Algol very
much. But that’s just me.

There are languages in which the program is written entirely
graphically, by joining boxes and arrows. I did all the programming for
my sleep study tracking in such a language, called Labview. Somebody
programmed the work that the boxes did, and the translation from the
graphical form to the assembler language (or perhaps compiler language)
form, but I thought (and graphed) in terms of signal flows and temporal
processing. Another purely graphical language I used for a while was
called Prograph. It had a lot to offer, but never really caught on.

I’m not at all clear what point you are trying to make in continuing to
demonstrate that you can write in Pascal what Tracy can write in J.
Tracy finds J more congenial and writing in it less error-prone for
what he wants to do. You find the opposite. Big deal.

For my part, I know that missing semicolons or writing semicolons where
colons are wanted (and vice-versa) tends to lead to quite a bit of
debugging, so I like a language with fewer symbols, particularly
syntactic symbols, to write. Labview was nice, because I had to write
very few. I just had to drag lines from output connectors to input
connectors, and to specify in letters and numbers some initial
conditions, parameters, and file names. A box in Labview could be a
Fourier Transform, a bandpass filter, a statistical operator such as a
correlation, or just a simple add or multiply operator. It would have
been possible to write the program for each box, but why bother? It
would be extra work, and probably wouldn’t be as efficient as the
provided algorithms when I finished debugging.

If you don’t want to try functional programming, don’t. You are fluent
in the lower-level language, and probably will get your results quicker
by using it.

Martin

···

At 11:53 AM 10/13/2008 -0700, Tracy Harms wrote:

[From Bill Powers (2008.10.14.0242 MDT)]

Martin Taylor 2008.10.13.23.45] –

Yes, and somebody had to write
the code to turn your Pascal program into assembler language for your
machine, and somebody had to write the code to turn that assembler
language into machine code. That’s the nature of compilers and
assemblers. They help a person with a computing problem use language
nearer to the language in which she thinks. There are many languages
because different kinds of problem domain are more easily expressed in
different kinds of language. I believe that R is a relative of J, but
slanted toward statistical computation. Among languages of the general
class of Pascal, I used to like Algol very much. But that’s just
me.

Yes, I used Algol, too. I started with assembler language as you did (not
counting toggling in the bits for booting up, as close as one gets to
true machine language). I agree that after enough of that, one longs for
a higher-level language, which first came along as a macro language,
still in assembler. My first real love was Turbo Pascal, written by Finns
(bought out by Borland) in assembler, compiling incredibly fast by the
standards of the day (and even now). When the TP executable code was too
slow, you could insert inline stretches of assembler code and optimize
the inner loops for speed. That’s still possible, though I haven’t kept
up with the instruction sets for modern chips, and anyway modern
compilers probably optimize better than I could.

There are languages in which the
program is written entirely graphically, by joining boxes and arrows. I
did all the programming for my sleep study tracking in such a language,
called Labview. Somebody programmed the work that the boxes did, and the
translation from the graphical form to the assembler language (or perhaps
compiler language) form, but I thought (and graphed) in terms of signal
flows and temporal processing. Another purely graphical language I used
for a while was called Prograph. It had a lot to offer, but never really
caught on.

Stella for the Mac was another graphic language, and Vensim, which I used
quite a lot ten or fifteen years ago is a good one for PCs (and had a
free version).

Then I wrote Simcon, and when Wolfgang Zocher joined that project we were
going to write our own graphical interface. The idea was to make
simulation-writing possible for people who weren’t programmers.
Unfortunately, Wolfgang was laid low by fibromyalgia and hasn’t been
active in the CSG since then. He actually had a real analog computer at
home, and used it to make sure that Simcon really worked like an analog
computer.

I’m not at all clear what point
you are trying to make in continuing to demonstrate that you can write in
Pascal what Tracy can write in J. Tracy finds J more congenial and
writing in it less error-prone for what he wants to do. You find the
opposite. Big deal.

No big deal – I was just trying to find out what I had missed, since I
hadn’t heard of “functional” programming. I’m pretty well
convinced now that it’s what I know as programming, but without the
lower-level stuff. I’m all in favor of that, as long as the preprogrammed
parts do what I want. I don’t like the very terse notation of J (or APL
etc), however, because I have trouble memorizing it and it’s not a good
way to teach newbies. I could see a use for a high-level language in
which multicharacter symbols that were self-explanatory were used, so a
person could learn to read such programs with relatively little study or
memorization.

I wonder – you’re good with foreign languages; I wonder if Tracy is,
too. My friend Kirk Sattley was a whiz at languages, and liked APL, and
LogLan, and Esperanto. Could it be that people who find languages in
general easy are better at learning the right associations, and simply
don’t see what the fuss is about learning a few hundred arbitrary and
otherwise neaningless symbols? I’m competent only in English and the more
English-like programming languages. Maybe you guys simply have some
abilities to make connections that I don’t have, or that work very slowly
for me.

For my part, I know that missing
semicolons or writing semicolons where colons are wanted (and vice-versa)
tends to lead to quite a bit of debugging, so I like a language with
fewer symbols, particularly syntactic symbols, to write. Labview was
nice, because I had to write very few. I just had to drag lines from
output connectors to input connectors, and to specify in letters and
numbers some initial conditions, parameters, and file names. A box in
Labview could be a Fourier Transform, a bandpass filter, a statistical
operator such as a correlation, or just a simple add or multiply
operator. It would have been possible to write the program for each box,
but why bother? It would be extra work, and probably wouldn’t be as
efficient as the provided algorithms when I finished
debugging.

I liked Vensim for similar reasons. I do a lot of debugging, as you say,
but I’ve had so much practice and made so many errors that I’m pretty
quick at it.

If you don’t want to try
functional programming, don’t. You are fluent in the lower-level
language, and probably will get your results quicker by using
it.

But I already do functional programming, as it turns out. I just write
the functions, too, and know enough about the lower-level details to
avoid constructions that entail a lot of overhead and slow the final
program (like too many function calls inside the inner loops).
Understanding machines isn’t a waste of time.

I like knowing how to do the lower-level stuff, too, because that gives
me a lot more flexibility than trying to adapt myself to someone else’s
idea of how to slice the pie. I have my own ways of doing things and
there are many functions I use now that I wrote a long time ago – but I
wrote them knowing about PCT, so they suit my uses. It’s amazing how
programmers can sneak their beliefs and superstitions into their products
– like the way the whole Gibsonian version of naive realism has
permeated Object Oriented Programming (methods are
“affordances” since they define what an object makes it
possible to do with that object).

Maybe the whole problem is that I have an aversion to lines of code that
look like the way they write swearing in cartoons.

Best,

Bill P.

[from Tracy B. Harms (2008-10-14 04:59 Pacific)]

Bill,

I'd like it very much if you would send to me the entire program text,
thank you. This is the sort of thing I've been meaning to get around
to working on. Thank you.

Tracy

···

On Mon, Oct 13, 2008 at 3:32 PM, Bill Powers <powers_w@frontier.net> wrote:

[From Bill Powers (2008.10.13.1502 MDT)]

...

I can send you the whole program if you want to try implementing it in J.

Best,

Bill P.