I fixed the flaw … and in so doing discovered a promising new equational programming paradigm.

The new paradigm allows a purely equational approach to something like OO and in particular allows an OO extension of Lucid that is simple and natural. But parametric programming (PP) seems to go beyond OO. It can be used in conjunction with other declarative languages like Haskell.

**Valof clauses**

First we need to avoid *where* clauses, which complicate the issues. When I was at Warwick in the old days there was a band of enthusiastic devotees of the pioneering system language BCPL. BCPL was a descendant of the monster (and unimplemented) language CPL, and an ancestor (via B) of C itself.

Anyway BCPL had many nice features including the VALOF construct. A VALOF was like a begin-end block except that you use a command RESULTIS to return a value. For example

VALOF $(
DISC := B*B - 4*A*C
IF DISC < 0 RESULTIS ERR
RESULTIS (-B + SQRT(DISC))/(2*A)
$)

BCPL’s VALOF is easily adapted to an equational approach. We simply replace the commands with an (unordered) set of equations including exactly one for the special variable *result*:

valof
disc = b*b - 4*a*c;
root = (-b + sqrt(disc))/(2*a);
result = if disc<0 then ERR else root fi;
end

(Note that the Lucid version, in which variables are evaluated on demand, will not attempt to take a negative square root. The equations are not assignment statements.)

**Output parameters**

At one point an interesting thought occurred to me, why not have output parameters? That return results? For example, this would allow an equivalent of *if-then-else-fi *without the nested structure:

valof
test = n<1;
choicet = 1;
choicef = n*(n-1);
result = choice;
end

The variable* result* is special in that it cannot be renamed – unlike *disc* and *root*. I call it a “parameter”, like the parameters *columns, rows *and* numformat* pyLucid uses to format output.

Here it’s understood that

`choice = if test then choicet else choicef fi`

but you don’t write this equation, it’s automatically included.

So that was my idea. I showed that with parameters you can formalize other “constructs” like multiple exit loops.

And that was it. Not an earth-shaking idea. And one with a few problems. For example, it ties up identifiers like “choice” or “test” that the programmer might want to use. So I dropped it, for many decades.

**A simple modification**

The other day I decided to write a blog post about parameters and as I reflected on the problem of tied-up identifiers a simple solution occurred to me: preface the parameter names with the names of the construct in question. Thus for example

valof
if.test = n < 1;
if.true = 1;
if.false = n*(n-1);
result = if.result
end

Well will you look at that. Familiar? Yes, it looks like the invocation of an OO object called “if”. The equations defining the ‘input’ parameters like “*if.test*” are setting properties (in this case *test*) and the expression *if.result* is calling a method *result*.

And yet there’s nothing going on other than invoking an implicit equation, namely

if.result = if if.test then if.true else if.false fi;

No storage allocation and no parameter initialization, none of the overhead associated with creating an object in traditional imperative OO. At most copying a string at compile time, namely the implicit equation.

**Schemes**

Clearly the *if* object is built-in. What about other objects we might want? Suppose we wanted to solve a quadratic equation. We can imagine invoking an object quad

```
valof
import quad;
quad.a = 3;
quad.b = 9;
quad.c = -8;
result = [%quad.root1,quad.root2%];
end
```

to calculate the two roots of the quadratic/;

3x^{2} + 9x - 8

Notice the line

`import quad;`

I put that there because I imagined *quad* would not be built-in and we’d have to invoke something to bring in the required implicit equations. And what would these equations be? Something like

```
disc = b*b - 4*a*c;
droot = sqrt(disc);
root1 = (-b + droot)/2/a
root2 = (-b - droot)/2/a
```

The obvious solution is

class quad
droot = sqrt(disc);
root1 = (-b + droot)/2/a
root2 = (-b - droot)/2/a
end

except there’s not enough information here. We need to specify that *a, b, *and* c *are input parameters (basically properties) and that *root1* and *root2* (but not *disc* and *droot*) are output parameters (basically methods). Also, as we will find, these packages can’t always be thought of as OO objects. So I’ve decided to call these sets of equations “schemes” as in

scheme quad
disc = $b*$b - 4*$a*$c;
droot = sqrt(disc);
$root1 = (-$b + droot)/2/$a
$root2 = (-$b - droot)/2/$a
end

The fact that *$root1 *and *$root2* have definitions means *root1* and *root2* are output parameters.

**Function Parameters**

So far so good. Then something hit me: both input and output parameters could be functions:

```
scheme integrate
m = ($a+$b)/2
$area = ($b-$a)($f($a)+4$f(m)+$f($b)
```

and could be used as follows:

```
valof
import integrate
integrate.a = 1;
integrate.b = 2;
integrate.f(x) = 1/x;
log2 = integrate.area;
end
```

This was a big surprise! pyLucid (for example) is strictly first order, you can’t define a function that takes another function as an argument. We figured out how to do it in principal but there are syntactical and implementation complications.

And now we have it almost for free, all done with little more than compile time string copying. We can also have functions returned as output parameters, as in this scheme which computes a derivative numerically:

```
scheme deriv
$df(x) = ($f(x+eps)-$f(x))/eps;
end
```

```
valof
import deriv;
deriv.f(x) = x*x;
result = deriv.df(4);
end
```

**Enter PYFL**

At this point it occurred to me that this idea has nothing inherently to do with Lucid, it works with any equational language. Furthermore as an equational language Lucid has irrelevant baggage such as being first order and requiring dimensional analysis to make eduction work.

So I decided to specify and implement a super simple equational language as a platform for demonstrating parameters. Thus was born PYFL, the PYthon based Functional Language.

What followed was ironic on two grounds. First, PYFL was not super simple for long. I discovered that it was relatively easy to extend PYFL with interesting features like VBOs or (paradoxical) functional while loops. At the same time, I got distracted from parameters and didn’t implement them till quite late.

And when I did implement them, I discovered that they aren’t quite as simple as they seem. You can’t just have the (say) equation for *if.result* at the topmost level. It has to be included in every *valof* clause that imports the *if* scheme.

User defined schemes are complex and haven’t been done yet.

So parameters aren’t quite vaporware but they haven’t really been tried out. The vapor hasn’t condensed yet.

]]>

A *while* loop? In a functional language? “Impossible!” you snort.

Well you’re wrong. Let me explain.

Anyone who has tried programming the Fibonacci function has got a big surprise. In PYFL, for example, you can naively write

`fib(n) = if n<2 then 1 else fib(n-1)+fib(n-2) fi;`

On my Intel Macbook Air evaluating fib(20) eventually produces 10946 but fib(30) hangs up. A little thought explains why: each call to fib generates two calls to fib … an exponential explosion, because the calls are independent, don’t share results unless you augment the simple program with some memoization scheme.

However in PYFL you have an alternative, namely a while loop:

```
fib(n) =
while i < n
i = 1 fby i+1;
f = 1 fby f+pf;
pf = 1 fby f;
result = f;
end;
```

Now when I evaluate fib(20) I get 10946 *immediately* and fib(30) produces 1346269, also immediately. Given fib(1000), after a barely detectable pause, I get

70330367711422815821835254877183549770181269836358732742604905087154537118196933579742249494562611733487750449241765991088186363265450223647106012053374121273867339111198139373125598767690091902245245323403501

The secret, of course, is that the while loop uses an iterative algorithm that carries forward both the current value (f) of fib(i) as well as the previous value (pf) of fib(i).

Isn’t this cheating? PYFL is supposed to be functional and use recursion not iteration … We’ll see.

Another example, summing up 22 terms of the series for e**x:

```
e(x) =
while i<22
i = 1 fby i+1;
term = 1 fby term*x/i;
sum = 0 fby term+sum;
result = sum;
end;
```

evaluating e(1) yields 2.7182818284590455, exact. Of course you can do this recursively but avoiding the calculation of x**i and i! at each stage requires auxiliary functions with extra arguments.

As for the charges of cheating – not guilty. PYFL while loops use plain old respectable tail recursion.

Suppose we want a function that removes duplicates in its list argument while preserving order. We soon realize we need an auxiliary function with an extra parameter that accumulates one copy of everything we’ve seen so far:

```
nodups(l) = g([],l);
g(m,k) = if k == [] then m else if hd(k) isin m
then g(m,tl(k))
else g(m<>[%hd(k)%],tl(k))
fi
fi;
```

Now consider how the recursion unfolds when l is [1 3 5 3]. We have

```
nodups([1 3 5 3]) => g([],[1 3 5 3]) => g([1],[3 5 3]) => g([1 3],[5 3]) =>
g([1 3 5],[3]) => g([1 3 5],[]) => [1 3 5]
```

And now notice that the variables m and k take on *sequences* of values; for example, m takes on the sequence

`[], [1], [1 3], [1 3 5], [1 3 5]`

And what are the *rules* that generate the sequences? For m the rules are that m is initially [], and thereafter is m again if the head of k is in m, otherwise m<>[%hd(k)%] (the result of appending the head of l on the end of k).

The rules for k are simpler: k is initially l, and thereafter tl(k), until k is empty.

This tail recursion corresponds closely to the (conventional imperative) while loop:

```
m := []
k := l
while k != []
m := if hd(k) isin m then m else m<>[%hd(k)%] fi
k := tl(k)
end
return m
```

Thus tail recursion and recurrence rules are basically different specifications of the same computation. The idea of PYFL’s paradoxical *while* loops is to let the programmer specify the recurrence rules for the iterative version of the computation then have the PYFL interpreter translate them into (functionally respectable) tail recursions.

Here is *nodups* as a PYFL while clause

```
nodups(l) =
while k != []
k = l fby tl(l);
m = [] fby if hd(k) isin m then m else m<>[%hd(k)%] fi;
result = m;
end;
```

The PYFL *while* is similar to the imperative one given above except that the statements are recurrence rules, not commands. They are unordered. The word *fby* is borrowed from Lucid but is just a syntax word, not an operator. It all gets translated into something very close to the tail recursion given above.

Ordinary tail recursions have a particularly simple formulation as while loops. Here is the definition of a function which sums the squares of the elements its list argument

```
sumsq(l) =
while m != []
m = l fby tl(m);
sum = 0 fby sum + hd(m)**2;
result = sum;
end;
```

The function *sumsq* can be defined using map-reduce:

```
sumsq(l) = reduce(map(lambda (h) h**2 end,l),add,0);
reduce(m,A,b) = if m == [] then b else A(hd(l),reduce(tl(m),A,b) fi;
```

which is nowhere near as clear.

Slightly more generally, the while loop

```
while p
a = fa fby na;
b = fb fby nb;
c = fc fby nc;
result = r;
end
```

gets translated to

```
valof
result = f(fa,fb,fc);
f(a,b,c) = if p then r else f(na,nb,nc) fi;
end
```

The rules in the body don’t have to be recurrences; you can define a variable directly as a given expression. For example we could rewrite our PYFL *while* as

```
while k != []
k = l fby tl(k);
h = hd(k);
m = [] fby if h isin m then m else m<>[%h%] fi;
result = m;
end
```

We need a slightly more sophisticated translation which I won’t go into.

Input and output calls may appear in a while loop, with the expected results.

Here is a simple loop for factoring numbers, which prints the partial results as the factoring proceeds

```
factors(n) =
while p*p <=
outputf('{:>20s}{:10d}{:>6} \n',str(f),m,p)
&& m
p = 2 fby if divides then p else p+1 fi;
m = n fby if divides then m div p else m fi;
f = [] fby if divides then p :: f else f fi;
divides = m mod p == 0;
result = outputf('{:>20s}{:10d} \n',str(m :: f),1) && "";
end
;
n = input('Number to be factored: ');
r = factors(n);
```

It works by enumerating possible divisors starting at 2, accumulating divisors found in the list f. If, for example, when it asks for an input number, you give it 1968, you get

```
[] 1968 2
[2] 984 2
[2 2] 492 2
[2 2 2] 246 2
[2 2 2 2] 123 2
[2 2 2 2] 123 3
[3 2 2 2 2] 41 3
[3 2 2 2 2] 41 4
[3 2 2 2 2] 41 5
[3 2 2 2 2] 41 6
[3 2 2 2 2] 41 7
[41 3 2 2 2 2] 1
```

There is another while-like construct, called *until*. It runs until a condition is true, in itself a trivial difference. But until has *two* conditions and *two* result expressions, the result returned depending on which condition is true first.

Here is an *until* clause used to calculate cube (for a change) roots using Newton’s method, exiting if it doesn’t converge in 8 steps.

```
root(N) =
until A == pA, steps>8
steps = 1 fby steps+1;
A = 1 fby (2*A+N/A**2)/3;
pA = 0 fby A;
result = A, "noroot";
end;
```

For example root(27) evaluates to 3.0 but root(100) produces *noroot*.

So why am I telling you all this? In the hope that you will switch from Haskell to PYFL?

Sorry, but I’m not delusional. PYFL is an experimental toy language but is far from practical for serious projects. I’ll make it available and some of you may find it interesting to write little programs with while loops, VBOs, interactive IO etc. Or experiment with your own ideas.

The real point is that serious functional languages, like Haskell, could benefit from these features – they are trivial to implement. The whole implementation of PYFL is less than 5000 lines of Python. For example, Haskell could have while loops using the source translation given above. The entire translation process, including parsing, required less than 100 lines of code.

Haskell is great but it shouldn’t be the last word in functional programming. There is no last word in functional programming, and the PYFL experiment makes that clear.

]]>For example, PYFL has a full set of Variable Binding Operators (VBOs) that use a linear ASCII form to represent the constructs like sigma notation of conventional mathematics. For example the following adds up the first 20 squares

`sumfor i in range(1,20) all i*i end`

and the following tests if N is prime

`forall d in range(2,sqrt(N)): N mod d != 0 end`

In the previous post I described the PYFL approach to input, which is (arguably) purely functional, drastically simpler, and trivial to implement, as compared to Haskell’s cumbersome IO monad. (Btw I have nothing against monads or Haskell in general, I’m just not impressed by the Haskell IO monad).

This time I’ll talk about PYFL’s output scheme. It ticks two of the three boxes just mentioned – it’s simple and easily implemented. Here’s some output (yep, Pascal’s triangle). I’ll show the program at the end of this post.

Unfortunately I can’t claim that PYFL output is functional. It shamelessly causes a side effect. I just don’t know how to do better.

A simple interactive program will illustrate the point. In BC in Canada at one stage you had to be a certain (varying) age to book an appointment for a Covid shot. This PYFL program asks your name and age and tells you whether you’re eligible.

```
query = if age>40
then outputs(name^', you are eligible!')
else outputs(name^', sorry, you are not eligible.')
fi;
name = input('Your name, please: ');
age = input(name^', your age please: ')
```

When *query* is evaluated the program will prompt for your name, then, addressing you by name, prompt you for your age, then tell you whether or not you’re eligible. (The carat symbol stands in PYFL for string concatenation.)

The ‘function’ *outputs* when evaluated prints its argument, which is normally a string. This is a blatant side effect. We can debate whether *input* invokes a side effect, too, but the important point is that they are easy to use and implement. And since invocations take the form of function calls they can be embedded in PYFL constructs like if-then-else-fi and VBOs.

The arguments to *input* and *outputs* can be any expressions. They don’t have to be string constants. In particular they can involve previous input values, as above.

There is also a more primitive output ‘function’ namely *output*, but *outputs* is usually preferred because it strips the the string quotes off its string argument, as does *input*.

The *outputs* pseudo function can be called embedded in PYFL constructs like VBOs, lambda expressions, if-then-else-fi’s and so on. Here is a program that prints a table of the first 10 squares and cubes:

```
res = concatfor i in range(1,10) all
outputs(i) && outputs(' ') &&
outputs(i*i) && outputs(' ') &&
outputs(i*i*i) && outputs('\n') &&
''
end;
```

(The operator && evaluates the first argument for its side effects, and discards the result.)

The result isn’t that great. We don’t have columns lined up. Fortunately we can do better. One of the advantage of using Python as the implementation language is that we have access to all its features in one way or another. In particular Python has a rather sophisticated output formatting system based on format strings with ‘holes’ for data. In PYFL you use another output pseudo function *outputf*. The first argument is a format string with holes, and the remaining arguments are data items that will be slotted into the holes. Format strings are well documented and what works for us is the string ‘{:5d} {:5d} {:5d}\n’.

```
res = concatfor i in range(1,10)) all
outputf('{:5d} {:5d} {:5d}\n',i,i*i,i*i*i) && ''
end;
```

The result is much better. Now the numbers are lined up in columns five characters wide.

The Python formatting strings can naturally handle floating point numbers as well. Suppose we want to print a table of square and cube roots of the first ten whole numbers but only want to display four significant digits. Here is the complete program

```
res = concatfor i in range(1,10) all
outputf('{:4d} {:8.4f} {:8.4f}\n',
i,sqrt(i),i**(1/3)) && ''
end;
```

and here is its output (I’ll deal with the empty string at the bottom later). There’s no need here to go into the details of Python format strings because they are well documented elsewhere.

Printing numbers in columns is one thing but producing a triangle is a bit harder – there’s nothing built-in to the Python formatting strings that would cover that. Instead we have to use the computational power of PYFL to generate appropriate side effects.

Here is the complete program

```
scc(L) =
while tl(M) != []
M = L <> [0] fby tl(M);
k = hd(M);
pk = 0 fby k;
n = k+pk;
N = [] fby n :: N;
result = n :: N;
end
;
tri =
while i<18
L = [1] fby scc(L);
i = 1 fby outputs(sp(35-( width(L) div 2))) &&
outputs(strlist(L)) &&
outputs('\n') &&
i+1;
result = L && '';
end
;
sp(j) = concatfor i in range(1,j) all ' ' end; // j spaces
width(L) = sumfor h in L all w(h)+1 end -1;
strlist(L) = concatfor h in L all str(h)^' ' end;
w(h) = 1 + (log10(h) div 1);
```

The first definition, of the function *scc*, defines a computation that generates the line of the triangle that succeeds a given line, both represented as lists. Thus *scc([1])* is *[1 1], scc([1 1]*) is *[1 2 1],* *scc([1 2 1])* is *[1 3 3 1]*, and so on. Yes, that’s a while loop – I’ll explain in the next post, but let me assure you in the mean time that’s all legit, no side effects.

The next definition, of *tri*, is another while loop. It repeatedly applies *scc* starting with *[1]* and generates 18 lines of the triangle. It converts these lists into strings and calculates their width. It reduces the left hand spacing as the lines grow wider.

I’m sure that by now you believe that as far as the terminal goes, PYFL can do anything reasonable (output strings can contain control characters and escape sequences).

However I’ve acquired a didactic debt, namely *while* (and other!) loops. I have some ‘splaining to do. Next post.

The basic language itself is pretty standard, except that it uses infix operators and conventional mathematical notation for function calling; e.g. f(h,a+2*y). It also has a wide range of variable binding operators; as in the program given at the end of this post

VBOs are great but the real elephant in the FP room is I/O. I have a few ideas.

**Monads schmonads**

Haskell uses the seriously complex machinery of monads to do I/O, supposedly without side effects (I don’t accept this). And you end up writing stuff like

`main = do {` ` ` `putStrLn "Hello, World!" ;` ` ` `return ()` ` ` `}` |

which to me looks like C. There must be a more functional approach.

I started from the PyLucid approach, that the output of a program is its value (as an expression) and its input is the value of a special variable *input*. Simple enough.

The problem with adopting this is that pyLucid has streams and PYFL doesn’t. Infinite lists could serve as a substitute but PYFL has only finite lists (to avoid the complications of list closures)

But there is no harm in having an *input* variable and this gives us a simple arrangement: the input becomes the value of *input*, and the corresponding value of the program is the corresponding output. Thus the expression

*input*input*

is a program to compute the square of its input (note that both occurrences of *input* denote the same value – no side effects, this program is equivalent to *input**2*).

What if we do want two inputs, and output their product? If one input variable is kosher, we can have two, *input* and *input2*. Our program is *input*input2.*

Generalizing, we could have *inputn* for every number n. Even better, a function *input(n)* of one number argument. When the computation (which is demand driven) needs the value of, say, *input(3)*, it could prompt for it. In this simplest form the scheme is impractical because in general there is no guarantee that these inputs will be demanded in numerical order; the computation may need *input(4)* before i*nput(3)*.

The solution (following Lucid) is to demand the inputs in order and cache them, so they will be available when the computation needs them.

**Input prompting**

PYFL goes one better. It has an input function whose argument can be an arbitrary string (not necessarily a numeral). This string is used as a prompt. Thus a program to compute the roots of a quadratic equation could be (and in PYFL is)

*valof** a = input(‘coefficient of x squared’);** b = input(‘coefficient of x’);** c = input(‘constant coefficient’)** rdisc = sqrt(b*b-4*a*c);** root1 = (-b + rdisc)/(2*a);** root2 = (-b – rdisc)/(2*a);** result = [% root1, root2 %];**end*

As it happens the computation will need the value of b before that of a. If this is a problem, we can write the definition of root1 as

*root1 = (1/(2*a))*(-b + rdisc);*

(later I’ll explain a more systematic approach). Then the demand for *root* generates a demand for *root1* which generates a demand for *1/(2*a)* and finally a demand for a. Demands for b and c follow shortly, in that order.

Input values are cached in a dictionary using the prompt itself as a key. Thus the implementation won’t ask you again for the coefficient of x squared.

This works well if there is a small finite set of inputs but what if, say, we wanted to sum the values of twenty inputs?

The answer is to generate prompts and embed the a call to the input function in a VBO that runs over the required range. For example

*sumfor i in range(1,20) all ** input(‘a number please(‘+str(i)+’)’)**end*

where *str* converts numbers to strings.

What if the input is being produced by another program? Why would we print the prompts? Because the program can read them digitally, and produce output as required. In other words the producer program is feeding our consumer program by producing its output on demand. It receives a prompt and computes or looks up the corresponding output.

We can save a lot of trouble if the producer knows ahead of time the order in which the input items will be required. Then we can eliminate the prompting dialogue.

The input scheme is not only conceptually simple (none of those pesky monads) but is trivial to implement – about a dozen lines of Python.

Now for output. The idea is … wait, take a look at the word count … this is already too long, the output story will have to wait till next time. Prompt me.

Here’s the promised program

*valof** subsets(s,i) = // the set of all i-element subsets of set s** if i==0 then {{}}** i==1 then collectfor x in s all {% x %} end** else** unionfor x in s all** valof** sminusx = s – {% x %}; // {% x %} is singleton x** withoutx = subsets(sminusx,i); // subsets without x** withx = collectfor u in subsets(sminusx,i-1) all //those with x** {% x %} ++ u** end;** result = withx ++ withoutx; // disjoint union** end** end** fi;** result = subsets({tom dick harry chris pat},3);** end*

which computes all the three element subsets of the set {tom,dick,harry,chris,pat} (PYFL has proper sets).

]]>A blessing because it has allowed many thousands to experience FP firsthand – by writing functional programs. They’ve discovered that it’s not that hard, that you can do useful projects in FP, and (thanks to the cleverness stuffed into Glasgow Haskell) the performance is comparable if not superior to that of conventional languages like Java,

So what is the curse? The curse is that Haskell dominates FP like Tyrannosaurus Rex dominated the dinosaurs (and like LISP used to dominate FP). Any discussion of FP becomes a discussion of Haskell, any proposed feature becomes a proposal for extending Haskell, and any problems with Haskell seem inherent in FP. The FP ecosystem seriously lacks diversity

I’m going to fix that with PYFL (PYthon based Functional Language), designed and implemented from scratch, as you watch

Take currying. In school we learned that addition, denoted by “+”, was a friendly little operator that takes two numeric values and returns their sum. Studying Haskell, we learn that it is actually a complex object that takes a number and returns a function that takes another number and returns the result of adding the first number to it.

Higher order functions that return other functions? Pretty complex stuff just to understand why 2+2 = 4.

Then there’s the monads. I’m more in favor of monads since I discovered that Lucid can be explained in terms of a simple stream monad. But I’m baffled by the IO monad. What causes the side effects? And are we really required to write imperative-looking code to read and write? Here is “Hello World” in Haskell according to online sources:

```
main :: IO ()
main = putStrLn "Hello, World!"
```

Yes, “putStrLn’, which, is clearly a command. The same source also offers

```
main = do {
putStrLn "Hello, World!" ;
return ()
}
```

which to me looks like C. Apparently the “do” is shorthand for invoking monads … who knows.

**Enough is enough**

I don’t believe functional programming should look like C and I’m going to put my money where my mouth is. I’m going to increase the diversity of the FP ecosystem by inventing a new FP language – starting now. As the title says, I aim to put the fun into – and take the C out of – functional programming. Perhaps I can overcome some of Haskell’s limitations and draw backs.

The new language is called PYFL : PYthon based Functional Language. It’s based on Python in the sense that Python is the implementation language.

Succeeding posts will take you through the development. You can think of it as live blogging except that I’ve already done much of the work.

I’m cutting only two corners. First, PYFL is dynamically typed. A static type system is too much work. And they can be constraining and cumbersome, so I think I can do without one for the time being.

The other corner is performance. PYFL is relatively slow given two levels interpretation. It would take a whole team to match the amazing work done on the GHC. But modern computers are powerful and PYFL performance is good enough that the language is usable on small projects.

**The basic principles**

The first principle is that PYFL is math. Out is any feature (like putStrLn) that can’t be explained *as* math. (And not just *through* math because any precisely defined notion can be described using math).

The second principle is that as far as possible PYFL uses conventional mathematical notation. This means prefix and infix operators, parentheses, and operator precedence. But most importantly, conventional mathematical notation adapted to linear format using the ASCII alphabet.

Thus a function to compute the root of the sum of the squares of its arguments can be defined by the equation

sumsq(x,y) = sqrt(x**2+y**2)

The function *sumsq* is a simple, humble first order function of two arguments (it has *arity* 2), not a disguised second order unary function. It is invoked like in your calculus text, e.g.

sumsq(3,4)

Of course we can define higher order functions as required, using function formal parameters or (linear ascii) lambda notation. Thus

simpson(f,a,b) = (b-a)*(f(a)+4*f((a+b)/2)+f(b))/6

Here *simpson(f,a,b)* is the simple Simpson’s rule approximation to the integral of function f from a to b. Thus

simpson(lambda (x) 1/x end,1,2)

gives an approximation to log 2.

**Valof clauses**

The only remaing feature of basic PYFL is a construct for auxiliary definitions. Haskell adopted Landin’s *where* clause, so that the definition of *simpson* could be written

simpson(f,a,b) = l*avgf

where l = b-a; m = (a+b)/2; avgf = (f(a)+4*f(m)+f(b)/6; end;

However *where* clauses cause problems with top-down parsing so instead we adapt BCPL’s *valof* construct, and write

*simpson(f,a,b) =** valof ** result = l*avgf; ** l = b-a; ** m = (a+b)/2; ** avgf = (f(a)+4*f(m)+f(b)/6; ** end*

The *valof* clause is equivalent to the *where* clause except for the definition of the special variable *result*, which is equated to the expression at the head of the corresponding *where* clause.

The equations in the body of a *valof* are unordered. There must be a definition of *result*, and every variable defined must have a unique definition. Definitions inside the body override definitions of the same variable outside the body. This is all standard. Thus

valof factorial(n) = if n<1 then 1 else n*factorial(n-1) fi; result = factorial(5); end

has value 120.

**VBOs**

PYFL fully implements Variable Binding Operators (VBOs), as described in a previous post. For example you can compute the set of all subsets of s as

*unionfor x in s all** valof** r = s – {% x %};** sr = subsets(r);** wix = collectfor u in sr all // subsets with x** {% x %} ++ u** end ;** wox = sr; // subsets without** result = {%s%} + wix ++ wox + {{}};** end**end*

A current list of VBOs is: sumfor, prodfor, consfor, appendfor, concatfor, unionfor, intersectfor, collectfor, those, exist, forall.

Notice PYFL has sets, implemented properly, with higher order primitives instead of a random member operator.

**The fixed point operator**

One intriguing property of basic PYFL (which is really just applied lambda calculus) is that in principal recursion is not necessary. The following *valof* also has value 120

*valof** alpha(f) = lambda (n) if n<1 then 1 else n*f(n-1) fi end;** Y(gamma) = ** valof ** result = h(h); ** h(k) = gamma(k(k)); ** end;** factorial = Y(alpha);** result = factorial(5)**end*

Examine the code carefully; you can verify that no variable is defined directly or indirectly in terms of itself.

Notice the simple (untypable) definition of the fixed point operator Y; there are advantages to skipping static type checking.

To see how Y works, look at the definition of h:

*h(k) = gamma(k(k))*

Now substitute h for k, giving

*h(h) = gamma(h(h))*

In other words, h(h) is a fixed point of *gamma*!

The PYFL interpreter handles this correctly; the *valof* above evaluates to 120. In fact all these examples have been tested by my PYFL interpreter.

There’s a lot more to PYFL but that’s for next time.

]]>In a previous post I showed how to find identify variables in a classic (time only) Lucid program that are constant – do not change with time. This information proves vital for output and for efficient caching.

Now we consider two dimensional Lucid, like pyLucid, in which variables can vary in two independent dimensions, time t and space s.

The situation is more complex because a variable can be constant, sensitive to time only, sensitive to space only, or sensitive to both. If we define the dimensionality of a variable as the set of all dimensions to which it is sensitive, the four possibilities are {}, {t}, {s}, and {s,t}. Our dimensionality analysis assigns one of these to every program variable, including the output. Note that these assignments are *upper bounds*. For example, we may assign {s,t} to X even though, when we actually run the program, it turns out that the space parameter doesn’t affect the results. Upper bounds are good enough to avoid caching duplicate values.

Here is a two dimensional program that produces the stream of prime numbers:

N2 = 2 sby N2+1;

S = N2 fby (S wherever (S mod init S != 0));

output = init S;

The first step, as before, is to convert it into a set of atomic equations:

N2 = 2 sby T00;

T00 = N2+1;

S = N2 fby T01;

T01 = S wherever T02;

T02 = S mod T03;

T03 = init S;

T04 = T02 != 0;

output = T03;

Next we set everything to dimensionality {} (constant). On the second iteration, we set N2 to {s} and S to {t}.

On the third iteration T00 gets {s}, S gets {t,s}, T01 gets {t}, T02 gets {t}, T03 gets {t}, T04 gets {t}, output gets {t}.

Finally on the fourth iteration N2 gets {s}, T00 gets {s}, S gets {t,s}, T01 gets {t,s}, T02 gets {t,s}, T03 gets {t}, T04 gets {t,s}, and output gets {t}.

The next stage gives the same result so the iteration is complete. We conclude in particular that N2 has dimensionality {s} (a pure vector), S has dimensionality {t,s} (a time varying array), and output has dimensionality {t} (a pure stream). When N2 is cached one space tag is enough, caching S requires a time tag and a space tag, and the output is a stream of scalars.

At each stage the new dimensionality of each variable is calculated from the previous dimensionalities according to rules for the operators. These rules are similar to the rules for time sensitivity but more elaborate. Here are some of them

if V = X sby Y then V is space sensitive, and also time sensitive if either X or Y are

if V = init X then V is space insensitive but time sensitive if X is

if V = X wherever P then X is space sensitive and time sensitive if either X or P is

if V = X + Y then V is space sensitive if either X or Y is and time sensitive if X or Y is

Each rule with operator o can be understood as applying a corresponding operator o* to the dimensionalities of the operands. For example {} sby* {t} is {t,s} and +* is union (of dimensionalities). These *-operators are all monotonic in the subset ordering of dimensionalities so that if d_{i} is the approximate dimensionality on the i^{th} iteration, d_{i} ⊆ d_{i+1}. This guarantees that the iterations eventually settle down.

**Multiple Dimensions**

What if we have more than two dimensions – say vertical v, horizontal h, forward and backward z? The algorithm should be evident by now. A dimensionality is a set (e.g.{t,h,v}) of dimensions, to which the intension in question is possibly sensitive.

Of course we don’t invent dozens of new operators like fby and sby. Instead we parameterize them with a dimension’s name attached with a period. Thus fby.h is fby in the vertical direction, and first.z is first in the forward/backward dimension. Ordinary fby becomes fby.t.

The algorithm proceeds as above, except that the values being combined are subsets of the set {t,s,v,h,z,…} of all dimensions. (We can assume, since any particular program is finite, that this set itself is finite.)

The general rules are obvious generalizations of those given above for {s,t}:

if V = X + Y then V is assigned the (set) union of the current values of X and Y

if V = next.d X then V is assigned the current value of X

if V = first.d X then V is assigned the current value of X, minus d if it is in this set

if V = A fby.d Y then X is assigned the union of the values of X and Y, plus d

if V = X whenever.d Y then X is assigned the union of the values of X and Y, plus d

**Future Work**

This analysis is by no means the last word for GLU, the advanced dialect of Lucid developed at SRI as a coordination language. GLU had many more powerful dimensional features. GLU had where clauses (blocks) with temporary declarations of local dimensions. It also allowed user-defined functions. These could be data functions like cube but could be nonpointwise, for example computing an running average. The definitions could be recursive and even dimensionally abstract, e.g. having a dimensional parameter that specifies the dimension in which the running average is performed.

At present we don’t know how to analyze such programs; research is continuing.

How did GLU do it? We don’t know because the GLU implementation is proprietary software and is currently locked up in a corporate vault. It is all but certain that it will never see the light of day. We do know, for example, from discussions with former GLU researchers, that they used crude approximations. In particular, for user defined functions they took the union of the approximate dimensionalities of actual parameters yielding a worst-case result.

]]>Dimensionality is a big issue with multidimensional Lucid – it means figuring out which dimensions are relevant to calculating the values of a variable.

It’s a simple idea but surprisingly subtle. In this post I’ll deal with the simplest case, time sensistivity in ‘classical’ time-only Lucid. Calculating which variables need to know the value of the time parameter *t*.

Here’s a simple program to compute the square root of the input (a floating point number) using Newton’s method

A asa abs(A*A – X) <eps

where

X = first input;

eps = 0.0001;

A = 1 fby (X+A/X)/2;

end

The first step (which the pyLucid interpreter takes) is to convert it to a set of *atomic equations*, in which each variable is defined as a single operator applied to arguments. The conversion involves introducing extra ‘temporary’ variables *T00, T01, T02*, …. etc:

output = A asa T00;

T00 = T01 < eps;

T01 = abs(T02);

T02 = T03 – X:

T03 = A * A;

X = first input;

eps = real 0.0001;

A = 1 fby T04;

T04 = T05 / 2

T05 = X + T06;

T06 = A / X;

The algorithm is iterative. It begins by assuming that none of the variables other than input are time sensitive and works through the equations accumulating variables that can’t be assumed to be constant.

In the first pass we notice that *A* is defined by *fby*, and any such definition is assumed to be time sensitive. (It may not be but this is a worst case analysis.)

On the other hand, on this first pass no other variables are added to our list. For example, *eps* is defined as a constant and *T02* is the difference of two variables that at this stage are still assumed to be constant.

On the next pass the time sensitivity of *A* is passed on to *T03* and *T0*6. Again, to be on the safe side, we have to assume that an arithmetic operation applied to variables at least one of which is time sensitive is itself time sensitive.

At this stage our list of possibly time sensitive variables is *A, T03, T0*6. The next pass adds *T02* and *T0*5 to the list and the pass after that adds *T04* and *T01*. Another pass adds *T00* but the next pass makes no changes.

We’re finished, and at this point we can be sure that the variables not in the list – *output*, *X*, and *eps* – are time constant. If they weren’t we would have found out by now.

Already this information is useful. It tells us the output of the program is a single constant. There is no need for the program to produce successive values of *output* because they will all be the same.

The general rules for the evaluation steps are very simple. Sensitivity is propagated (or not) depending on the operator. In particular

if V = first X then V remains insensitive

if V = next X then V becomes sensitive if X is sensitive

if V = X fby Y then V becomes sensitive

if V = X asa P then V remains insensitive

if V = X + Y and either X or Y is sensitive then V becomes sensitive

(and the same for multiplication, division, and any other data operations including if-then-else-fi)

input is time sensitive

The reevaluation proceeds until it settles down; until no new sensitive variables are found.

In addition to allowing us to simplify output when the variable *output* is constant, it allows us to save space when caching. The values of variables like *A* above are cached and in so doing each value is tagged with the value of the *t* coordinate – because different values correspond to different timepoints. But this is not necessary with a variable like *X* which is known to be constant. If we don’t take this information into account we will store many copies of the same value.

Dimensionality analysis is simple enough when we’re dealing just with the time dimension. Adding even one extra dimension – say space *s* – seriously complicates the analysis, as we will see.

The most pervasive fallacy of philosophic thinking goes back to neglect of context.

What exactly is “intensional programming?” The easy answer is, programming in a language based on intensional logic. But that raises another, more important question, namely what is intensional logic? Logicians have been working on the answer for more than 2500 years.

The short answer is, logic in which the truth-value and more generally the meaning of an expression depends on an implicit context. Let me attempt to give you the full answer.

The term “intensional” itself is relatively recent. Carnap introduced it in the 1930s, based on Frege’s distinction between the “sense” and the “denotation” of an expression. In Frege’s terminology the denotation of an expression is just that – the particular object it (currently) denotes. (This is what Carnap and modern logicians call the “extension”). For example, “the President of France” currently [2021] denotes Emmanuel Macron.

On the other hand the “sense” (what we now call the “intension”) is the entire concept it represents – what we, at some level, intend when we write it. No one would claim that M Macron somehow sums up the whole concept of the French presidency.

Intensional logic is therefore the logic of expressions in which the intensions must be taken into account. These are very common in natural language. The French constitution specifies that the President is directly elected; and this is not the same as specifying that M Macron be directly elected.

**The mysteries of intensionality.**

Many famous paradoxes are based on the observation that intensional expressions seem to violate the basic law of substitution of equals for equals.

According to the latest announcements,

The number of planets = 8

Kepler, the famous astronomer, was well aware of the basic rules of arithmetic. We can be sure that

Kepler knew that 8 is a perfect cube

But if we substitute equals for equals in this latter assertion, we get

Kepler knew that the number of planets is a perfect cube

which is almost certainly false.

In one sense, the explanation is simple: the equation relates only the extensions of the two expressions, whereas “Kepler knew …” refers to the intension of “the number of planets”. But what sort of mathematical object is an intension?

**Necessity and Possibility**

Aristotle, the founder of formal logic, first addressed similar problems. It is often said that Aristotle’s logic is two-valued, but this is not correct. He carefully distinguished between assertions that are true but not necessarily so, and those that are true by necessity – that could not possibly be false. Aristotle classified these different ‘modes’ of truth and falsity He tried to extend his analysis of syllogisms to include those in which assumptions and/or conclusions were not simply true, but necessarily true, or only possibly true.

Necessity is, in our terminology, an *intensional* operator. We cannot always determine the truth of “necessarily P” knowing only the truth (extension) of P. P may be true but not necessarily so.

The Greek Stoics and the medieval Scholastics continued the tradition. During this entire period of more than two millennia, “modal” logic (the logic of necessity and possibility) was considered to be an integral part of formal logic.

Around 1900 Frege, Cantor, and Russell completed Leibniz’ program of mechanizing logic. Set theory and the predicate calculus are entirely extensional formalisms and deal with unchanging, immortal entities encountering each other in an empty context. Extensional logic proved very successful and Russell and Wittgenstein wasted no time generalizing their approach to to a whole philosophy, *logical atomism*. In this philosophy knowledge – in fact reality itself – can be described as a large collection of atomic facts evaluated in isolation.

Frege was aware that something (namely intensions) had been omitted. Efforts began almost immediately to extend mathematical logic to cover intensional phenomena (as they were later called).

**Lewis and Langford formalize Modal Logic**

The effort to recover modal logic began soon after, in the 1930s, when CI Lewis formalized his logic of “material implication”. He quickly dropped this, however, in favor of necessity and possibility. For these he introduced the symbols ☐ and <> which have since become standard.

One of the challenges was that it was not completely obvious which axioms are needed to capture the notions of necessity and possibility. For example, Aristotle noted that everything that is necessary is possible, i.e.

☐P → <>P

More generally, everything that is necessary is in fact true; and everything that is in fact true must clearly be possible. Hence

☐P →P

and

P → <>P

On the other hand, if P and Q are both possible, we cannot assume that they can be true simultaneously; so

(<>P /\ <>Q) → <>(P /\ Q)

cannot be an axiom or even a theorem.

Some principles, however, are hard to decide. If something is possible, is it necessary that it be so? In other words, should

<>P → ☐<>P

be a tautology? Lewis and Langford avoided ruling on such contentious issues and instead developed a series (S1 through S5) of (propositional) modal logic systems based on increasingly more powerful axioms. Since then logicians have proposed literally dozens of different theories, and the whole collection is far from being linearly ordered.

The syntactic approach confirmed that modal logic is not inherent nonsense; Lewis and others who followed showed that their systems are consistent, and do not collapse the modal operators onto the identity operator. Nevertheless, their system had no model theory (no semantics in computer science terminology).

As a result, for at least another three decades modal logic lacked the respectability of extensional logic.

**Kripke’s possible worlds semantics for modal logic**

Finally in the early sixties Saul Kripke presented the first completely formal semantics for modal logic. This semantics was based on the notion of “possible world” or “alternate state of affairs”, which can be traced back nearly a thousand years, to the medieval logician Duns Scotus. For Scotus, an event is possible means that we can imagine it taking place without contradiction in at least one alternate state of affairs. Conversely, something is necessary if it remains true in all the alternate states of affairs.

Kripke’s contribution (anticipated by Church and Carnap) was to completely avoid the whole question of what a possible world is and to take it to be an undefined concept. A Kripke model is simply an indexed family of normal interpretations, one for each possible world. The only structure on the universe of possible worlds is a binary relation that specifies which worlds are possible alternatives

to a single given world (this is called the *accessibility relation*).

In Kripke semantics, propositions are not normally either true or false; they are true in some worlds and false in others. The Kripke semantics of necessity and possibility formalizes the “alternate state of affairs” idea of Scotus. A proposition of the form ☐P is true at a world w iff P is true at *all* worlds w’ accessible from w. Dually, a proposition of the form <>P is true at a world w iff P is true at at *some* world w’ accessible from w.

Kripke showed that most of the proposed modal axioms corresponded to different assumptions about the accessibility ordering. For example, if everything necessary is to be true, and everything true is to be possible, we require that the relation be reflexive (so that every world is an alternate to itself). If the ordering is transitive, we have

☐P → ☐☐P

so that everything necessary is necessarily so.

Scott’s semantics for intensional logic

Once the notion of possible world/context was formalized, it was easy to find new intensional formalisms that went beyond the traditional necessity/possibility.

The most important generalization, however, was to break any remaining ties with the ancient modalities and consider operators not defined by an accessibility relation. This means giving a semantics not just to modal logic, but to intensional logic in general. Credit for this primarily goes to Dana Scott. In 1969 he laid out a complete framework for a semantics of intensional logic in his (perhaps mistitled) *Advice to Modal Logicians*.

In *Advice* Scott takes the basic idea of Kripke models and extends it to give a framework in which Carnap’s distinction between intension and extension can be formalized. In a “Scott model” we have a nonempty set I of reference points (essentially, possible worlds) but do not require that any accessibility relations be specified. Whatever the syntactic details of our language, propositions are not a priori absolutely true or false Their truth value varies from world to world; although they may happen to have the same value at each world. Scott calls the truth value of φ at a particular world the *extension* of φ at that world. The *intension* of φ, on the other hand, he takes to be the function that maps each world w to the extension of φ at w. In other words, the intension of a formula is an element of 2^{I}, 2 being the set {0,1} of truth values.

In Scott’s approach, a (unary) intensional *operator* is simply a function that maps 2^{I} to 2^{I}. It may be defined in terms of an accessibility relation, but the framework allows for more general kinds of operators. As an example, he proposes a formalization of the present progressive tense (as in “I am eating”). He takes I to be the reals and defines [H]E to be true at time t iff E is true in some interval (however small) containing t.

Scott models are not restricted to propositional logics. They can also specify a collection D of

individuals that serve as the extensions of terms. These individuals can be (understood to be) numbers, strings and lists, physical objects, people, organizations, etc. However intensional individuals denote elements of D^{I} — they may have different extensions at different worlds. The D^{I} are intensional objects elements or virtual individuals. They correspond to natural language phrases such as “the President of France” and Scott models can give us a clear explanation of the puzzles described at the beginning of this article.

For Scott an important challenge was dealing with intensional individuals that don’t exist at certain worlds. This has not proved (so far) to be a problem for intensional programming. For example, we assume the string “Hello World” is always available.

**Intensional Programming**

The Scott (—Montague—Kripke—Carnap—Lewis) approach to modal and intensional logic has proved very successful in illuminating the foundations of natural language. For me, however, the real significance of the Scott approach is that it laid the basis for Lucid and intensional programming.

For example, consider the case of a time-varying two dimensional intensional spread sheet. We define a smoothing operator that calculates at each cell and timepoint the previous value of the average of the surrounding cells:

*smooth(S) = prev (up S + right S + left S + down S)/4*

The intent is clear enough but without a formalization of intensional logic what would we make of such definitions? The operator *smooth* is an intensional operator that acts on intensions that are maps from

*time x space x space -> floatingpoints*

**The Creation of Intensional Programming**

This is not the place to present even a brief overview of work in intensional programming. However, We can explain the connection by presenting a Creation Myth that makes the connection clear (and also puts its creators in a more favorable light). According to the Myth, far sighted Computer Science researchers read and understood Advice and decided to use it prescriptively, as the basis for new programming languages and systems. The first language was Lucid,invented by the author and E. A. Ashcroft in 1974. (Now you know who the far sighted researchers were. However the Myth does not explain why they waited five years.)

The Creators chose (a family of) Scott models in which I is the set of natural numbers, interpreted as timepoints. For the individuals, they took (in the simplest case) D to be the set of integers. Their language included the operators *first*, *next* and *fby*, with *next* (for example) the intensional operator

from D^{I} → D^{I} such that

next(X) = λn X(n+1)

Programs consisted of equations defining program variables in terms of constants and each other.

Program variables were therefore intensional integers.

At this point the Myth has the Creators look at what they had done, and see that it was Good (a few referee reports aside). They return for more Advice and found it; for example,

This situation is easily appreciated where I is the context of time-dependent statements; that is in the case where I represents the instants of time. For more general situations one must not think of the elements of I as anything as simple as instants of time or even possible worlds. In general we will have i= (w,t,p,a) where the index i has coordinates; for example w is a world, t is a time, p = (x, y, z) is a (3-dimensional) position in the world, a is an agent, etc. All these coordinates can be varied, possibly independently, and thus affect the truth of statements which have indirect references to these coordinates.

The newly inspired Creators (by now a much larger group) first added extra time dimensions to Lucid to allow nested iterations. Then space dimensions, used much like arrays. The “place” dimension (actually a form of branching time) allowed programs with first-order recursive functions to be translated into pure intensional programs. Later, spreadsheet and attribute grammar tools used cell and tree node coordinates, respectively. Branching time allowed logic programs to express search strategies in a very simple way. We also discovered a translation scheme for some higher-order programs using multiple-place coordinates.

Myth meets reality

Intensional programming obviously developed quickly and rationally in the parallel universe of the mythical Creators. Over in the real world, however, the real creation was a longer and less orderly process. To be fair to the real creators, they had some serious obstacles to overcome, and in so doing they had to extend the system of Advice in important ways. From the beginning they — we — wanted to allow recursive definitions of operators. This meant that the collection of intensions had to form a domain (yes, a Scott domain) and that the basic operators had to be continuous over this domain. In particular, this meant that the operators be finitary: that any particular extension of the result of an operation can be computed from a finite set of extensions of the operands.

Intensional programming also used multiple dimensions almost from the beginning. It took a long time, however to realize that it was not enough to have (even a large) set of fixed predefined dimensions; or even an infinite, indexed collection of predefined dimensions. Eventually, the GLU system allowed user-declarable local dimensions.

Finally, the Creators realized the language could be implemented through a demand—driven dataflow model, in which demands for particular extensions of variables can generate demands for other particular extensions**From philosophy to technology**

The system presented in Advice has proved to be almost exactly that needed as foundation for intensional programming; we have made no changes, and very few additions. It has passed the hardest design test of all, namely suitability a for a purpose (intensional programming) that could not be foreseen. The same, in fact, can be said for intensional logic as a whole. It has developed in much the same way as other, very different technologies. It began with the study of a few curious phenomena and a collection of baffling paradoxes. A long period of study and experimentation, much of it apparently futile, eventually led to understanding and to the perfection of useful tools.

We have now reached the stage where, in Scott’s words “the old puzzles can be cast aside, and one can begin to provide meaningful applications.”

[*This is the infamous section of the book *Lucid the Dataflow Programming Language* where I make fun of everyone working on imperative languages. It was very popular but many people hated it even though no individual is named. In a companion post I cite it as an example of a *Career Limiting Move*. It didn’t quite kill my career though it didn’t help. I’m sure there were a number of meetings I wasn’t invited to and program committees I was left out of because of it. Hmmm … was that really a bad thing?*]

[*Btw the late Ed Ashcroft was a co-author of the book but had no part in writing this section. Wise man.*]

**1.5 Some Suggested Solutions**

Even the most enthusiastic supporter of imperative languages will admit that something is very wrong. There are, however, a number of different attitudes regarding the “software crisis” and the “parallelism problem”. Here are some of the more common points of view (slightly parodied). [slightly!]

First there is what we might call the **Clint Eastwood outlook**.

[*This holds up well – though it was written more than 30 years ago!*]

According to this “rugged Western” point of view, programming is a demanding and difficult activity, and always will be, but still “a programmer’s gotta do what a programmer’s gotta do”. According to the ‘**Cowboys**’ the actual problem is a shortage of real programmers who are clever enough and tough enough to get the job done. The Cowboys learned programming the hard way, through long hours ‘in the saddle’ (i.e., at the terminal) and expect everyone else to do the same. If you make mistakes, it is ‘yer own darned fault’. Cowboys generally distrust ‘book larnin’ (theory) and ‘new-fangled’ languages. They sneer at ‘greenhorns’ who complain that PL/I

[*PL/I? A big complex language devised by IBM and long since dead. More complicated than C++ and clumsy like ADA. ADA? … don’t ask.*]

is too big or that C is too low level.

[*Yes, C, the same C that is still popular today. Basically unchanged, like Clint himself*]

To them, PL/I does not have enough features and C is not dirty enough. What they want is even more powerful tools which allow them to get even closer to the computer and make it ‘dance’.

The Cowboys certainly deserve our admiration. The best of them (the real Straight Shooters) can produce impressive software that ordinary programmers are too ‘chicken’ even to try. Nevertheless, we have to ask ourselves whether or not there are enough really tough Wranglers to go around. There almost certainly are not, and we have to resign ourselves to the fact that most software will be written by ordinary, humble Trail Hands

[*I just described typical software engineers as “trail hands”. I’m sure that went down well.*]

—not by the Wyatt Earps of the terminal.

Next we have what we call the “**Mr. Wizard**” school of thought.

[*Mr Wizard was a well known TV science popularizer a few decades ago. He was the Bill Nye of his day.*]

The Wizards are in many ways the opposite of the Cowboys, but they agree on one thing: programming and programming languages are inherently very complex. The problem, as the Wizards see it, is that we lack a good theoretical/mathematical understanding of these languages. The Wizards have searched through many a dusty tome of ancient lore. They have learned the Arts Magical, the Logyck Symbolyck and the Calculus of Lambda. They have conjured up the mighty names of Tarski, Church and Curry.

[*Famous logicians. I actually met the first two as a grad student at Berkeley. In fact I took a seminar from Tarski.*]

The ultimate dream of the Wizards is a system for formal program verification. This would allow programmers (assisted by Wizards) to produce airtight proofs that given programs are correct—no matter how appallingly bad the language or program might be. Program verification is therefore a kind of philosopher’s stone which will turn base programs into gold. As John McCarthy [*inventor of LISP*] (1965, p. 219) said,

The prize to be won if we can develop a reasonable mathematical theory of computation is the elimination of debugging. Instead, a programmer will present a computer-checked proof that the program has the desired properties.

The problem, of course, is that no amount of Wizards’ wand waving changes the basic nature of the von Neumann [*imperative*] languages. The Wizards did succeed in producing formal specifications—but these specifications also took on telephone-book proportions, full of incomprehensible lambda- calculus expressions.

[*This was semantics of programming languages, a big deal in its day. As dead now as PL/I*]

Similarly, proving the correctness of ‘real’ programs has also turned out to be impractical. [*still is, with specialized exceptions.*] The Wizards ran into trouble with the ‘dirty’ features (side effects, goto statements, aliasing) mentioned. Of course, these are exactly the features the Cowboys love the most.

[*Well, the *goto* has almost completely disappeared.*]

There is a third group, however, which views the antics of the Cowboys and Wizards with self-righteous disdain. The members of this group are wild-eyed fanatics, the **Preachers** of the gospel of structured programming.

The Preachers subscribe to a computer science version of the doctrine of Original Sin. They believe that human beings are born with an inherent tendency to be careless and make mistakes (the sin of Sloth), and to undertake tasks that are beyond the meagre powers of their mortal minds (the sin of Pride). The wages of sin are, of course, bugs. If bugs (and software problems in general) are to be avoided, programmers must abandon their evil ways and adopt the way of the righteous. Programmers are exhorted to adopt some rigorous discipline (methodology) of programming whose rituals will prevent the software from being possessed by evil spirits.

[*Nobody talks about “structured programming” any more because everybody practices it. But other cults have arisen.*]

The disciples of structured programming have indeed achieved important successes in the fight against bad software. Nevertheless the evil that lurks within the hearts of men and women has proved harder to vanquish than was expected. Abstention plays an important role in the Preachers’ teachings—abstention from features like *goto* statements and pointer variables, those forbidden apples that tempt programmers into wickedness. Programmers, however, have great difficulty in avoiding these forbidden fruits, as we have already noticed. The Preachers dream of a pure and holy language from which all wicked features shall be banished. In practice, though, the designers of these ‘structured-programming’ languages are always in the end forced to compromise their principles in the name of efficiency. The faithful are therefore faced with a terrible but unavoidable dilemma: they can choose to be virtuous and write programs that are correct but inefficient, or they can choose to be wicked and produce programs that are efficient but bug ridden. “Why,” the Preachers must ask themselves, “does the Devil have all the fast programs?”

There is, however, a fourth and more optimistic point of view which shares the Preacher’s low opinion of unaided human capabilities but which offers a more down-to-earth solution. We are referring to the “we have the technology” school of thought.

The **Boffins** agree that human weakness is the root cause of the software crisis, and that therefore mere humans cannot produce software naked and unarmed.

[* “Boffin” is British slang for a techy – remember, the book was written and published in the UK. Radar, for example, was invented by Boffins.*]

The armament the Boffins offer, however, is physical and mechanical, not moral and spiritual. Their goal is to supply the programmer with a complete selection of powerful programming tools: structured editors, diagnostic compilers, clever type checkers, and sophisticated debugging systems. Their aim is to produce a kind of Bionic Programmer, a technologically enhanced Superhero who can battle the bugs on equal terms.

[*The Bionic Man was a US TV character who was a cyber enhanced man with telescope eyes, steel legs etc. Sort of like Robocop or Ironman.*]

There is no denying, of course, that the ingenious devices found on the ‘programmer’s work- bench’ can be extremely useful. The problem is that they can amplify programmers’ weaknesses as well as their strengths. The gadgets in the Million Dollar Programmer’s

[*the program about the bionic man was called The Million Dollar Man. That was when a million dollars was a lot of money.*]

utility belt [*some mixed metaphors here*] can just as easily allow their user to produce colossal bug-infested programs and then allow him to waste stupendous quantities of resources in debugging. Could the superprograms produced by the new generation of Superprogrammers breed a new race of terrifying superbugs? Will programmers need even more powerful tools as a result? Will these tools generate even more terrifying bugs? There could be no end to the process!

[*There isn’t. I have two friends in the industry each maintaining legacy programs millions of lines long*]

The ambitions of what we call the “DIY” or “**Mr. Fixit**” school are by contrast far more modest. According to this point of view, the software crisis is the result of serious flaws in the imperative languages. These flaws are not, however, the result of weaknesses inherent in the whole von Neumann approach; but rather the product of design error. All that is needed is a little tinkering, a few new parts here and there, the removal of a few troublesome features, and all will be well.

[*Python 3.8 introduces the “walrus” operator. That should set things straight.*]

Now it is certainly true that the imperative languages can be improved, and often fairly simple changes can bring great benefits. The problem is that one change leads to another—and an improvement in one area can have disastrous consequences in another. We have already seen that tinkering with ALGOL and FORTRAN has not always been successful.

[*ALGOL? A predecessor of PL/I. FORTRAN is still FORTRAN, though now it looks like C*]

This is especially true when an attempt is made to bolt on features for ‘concurrency’ or even ‘dataflow’.

[*All in all I think this holds up pretty well, including even the cultural references. The cowboys, wizards, boffins etc have come up with some great stuff but software projects are still typically late (or unfinished), much bigger than planned, over budget, and full of bugs.*]

[*The next section is basically a grovelling apology where I explain that these criticisms are just “funnin”. Unfortunately I used the word “criticism”, which is at the heart of most CLMs. Career – limited*]

Sha la la la la la la

La la la

La di da

La di da

Van Morrison (Brown Eyed Girl)

A lot of people have to write as part of their jobs – grant proposals, progress reports, specifications. And there are endless verbal communications – defending code, disputes over features, justifying organization changes, technology explanations, and so on forever.

Well, the good news is that Hollywood can help!

A lot of people find this all very challenging, and, as part of a typical business/ technical education, have no training in writing and talking persuasively. Sometimes they have been told that laying out the facts in their favor will be enough.

Well it isn’t, as they soon find out. What they need is rules, guidelines, even formulas for persuasive communications.

There are such rules, those of traditional *rhetoric*, which are actually taught to lawyers and communications professionals. But not to engineers or mathematicians.

I’ll talk about rhetoric in a future post but here I want to go beyond rhetoric, because in general it’s not enough. In particular it’s of no use for the most persuasive form of communication: stories.

Phil Ochs once said that a good (protest) song is worth a thousand rallies. Stories can be very effective but where do they come from? Are there rules?

There sure are. If you think about Hollywood’s creations, the first word that comes to mind is “formulaic”. In fact the former script writer Robert McKee wrote a famous book, *Story*, which lays out many of these formulas, and it became a bible for at least some aspiring Hollywood writers.

*Story* is not easy reading and of course was never intended to help with professional writing. Can we do better? Yes, that’s why I’m here.

**A story**

But first I want to tell my own short story. Not so long ago I came up with a theory of stories. The idea is that stories have profiles based on the variation of the protagonist’s fortunes. I was very pleased with myself.

Then a post appeared in Hacker News which described a theory developed by the famous author Kurt Vonnegut for his Master’s thesis (which was rejected!). It used graphs like for polynomials or sine waves to capture what he called the “shape” of a story, based on the rise and fall of the protagonist’s fortunes.

The HN post included a link to a video of Vonnegut explaining his theory with example shapes, like that of *Cinderella*.

I’d been scooped by Vonnegut, by decades.

I was quite depressed by this discovery. Then two things occurred to me. First, the fact that Vonnegut, a successful author, is advancing the theory gives it credibility. I’ve never written fiction successful or otherwise. Who’d listen if it were just me saying it?

More importantly, I soon discovered that Vonnegut had done nothing with his discovery. I found a video of a talk that Vonnegut had given at least twenty years earlier. It was the same talk.

Finally, it became clear to me that my version of Vonnegut’s theory could be applied to professional writing. I had something to say after all. I felt pretty good in the end and began working on this blog post.

**The basic pattern**

That’s the story – it’s true – and it also illustrates the most basic formula, what I call the *double reversal*. (Some philosophers call it the *negation of the negation*.)

I discovered the pattern (for myself) by thinking about the best known Hollywood formula, namely *boy meets girl*.

You know how it goes. The protagonist, boy, meets a girl (or girl meets a boy, or a boy meets a boy etc) and they hit it off. They are happy for a while but then because of an argument or a misunderstanding or whatever boy loses girl.

Boy is very unhappy and tries various schemes to get girl back. Eventually something works and they are reunited, on a firmer basis than before. They end up happier than ever.

I thought up a phrase that summarizes the whole trope, namely

La-di-da; oh shoot; la-di-da.

Meaning at first everything is fine, if not downright good; then suddenly things are bad for whatever reason; then the bad stuff ends and all is well again.

This pattern has almost universal application, including to my little story. At first I’m happy having discovered the idea of story profiles. Then I learned that Vonnegut’s shapes are the same idea and felt bummed out – oh shoot. But then it dawns on me that profiles can be used for professional writing and I’m all *la di da*.

**La di da fund me**

What the shapes idea reveals about professional writing is that it is (usually) relentlessly positive. A typical grant proposal emphasizes a wonderful track record, a strong team, and solid ideas for future work. La di da, la di da, la di da di da.

Theoretically (according to the rules of rhetoric) this is persuasive but I’m not so sure any more. It doesn’t make a good story. Good stories need some form of adversity or opposition to be overcome. Who’s interested in a story about an invincible superhero who leaps unopposed from one achievement to the next? For that matter, who believes it?

It’s easy to rework a grant proposal along the lines of the basic trope. First you describe the basic problem and the partial results you and others have had. La-di-da.

Then describe running into a serious problem that the standard methods won’t handle. Oh shoot. Admit that you were stumped at first and even wondered if any solution existed (don’t overdo this part).

Then explain how you in fact solved it and how much improved the situation is (la-di-da) and how much you look forward to doing all kinds of things with your new grant.

**The Lambdi-da calculus**

Writing out la-di-da gets annoying and tiresome so I invented a simpler notation. La-di-da is simply “+” and oh shoot is “-“. Blanks separate what writers call “acts”. So the basic double reversal pattern is + – +.

The simple version expresses only the sign (good or bad) but not the intensity. We can express the intensity with repetition, so that “++” is la-di-da-di-da and “— is shoot-and-rats.

We’ll use parentheses for nested plots. Suppose girl meets boy, girl loses boy, girl tries three promising things to get boy back, the last of which works. This gives us

+ -(- + – – + – – + +) +

Here each individual unsuccessful attempt to get girl back is – + -, meaning struggling for a solution, temporary optimism as the plan is put in effect, then pessimism as it fails. The third ends with la-di-da success and the whole episode ends la-di-da.

[Oh shoot, a commenter points me to the *W-plot* idea by Mary Carroll Moore. Basically a W plot is a simple version of the above where boy meets girl, boy loses girl, boy devises scheme to get her back, scheme is a disaster, depths of despair, then somehow gets her back anyway and la-di-da. The lambdi-da formula is ++ -(++ —- +++) ++++ more or less. Oh, la-di-da, this doesn’t scoop the lambdi-da calculus, which is more general. Moore’s made a career out of one formula … maybe I’m on to something!]

**Bringing them back for more**

Grant applicants and TV series show runners face a common problem, namely follow-up. How do you persuade viewers to come back for the next episode? How do you persuade granting agencies to continue funding?

Hollywood has come up with at least two successful formulas: the *happy ending *and the *cliff hanger*.

With the happy ending, the action ends with a strong la-di-da scene, often after intense oh shoot action. The lambdi-da expression, with repetition for intensity, might be as simple as ++ — ++++.

The idea is that the transition — ++++ gives you so much joy and relief that you want more, and will tune in next week. The only risk is that tolerance develops and periodically you have to ramp up the intensity of the oh shoot / la-di-da transition. Eventually this leads to plot inflation and jumping the shark.

An example of a happy ending is the final musical numbers in the film Pitch Perfect. It was so joyous it brought back audiences for Pitch Perfect 2, with mixed results.

The other technique, the cliff hanger, means ending the episode on intense, and usually surprise, adversity. Often this follows a la-di-da session that they naively believe to be the end of the matter.

A typical Lambdi-da expression might be ++ — ++++ —–.

The idea is that the viewers expect from experience that in Hollywood oh shoot is inevitably followed by a more intense la-di-da and they don’t want to miss it.

A recent example of a cliff hanger is the second last episode of season two of the Mandalorian, where tiny adorable Grogu is kidnapped by Moff Gideon’s evil red-eyed Dark Troopers.

The danger with the cliff hanger is that the last negative session may put the audience off. The promise of more la-di-da may not be enough to compensate for more intense oh shoot that precedes it.

Both these techniques have this in common, they promise la-di-da , either as more of the same (happy ending) or the inevitable reversal (cliff hanger).

**And I propose …**

What about grant applications, bonus requests, promotion applications and the like?

The relentlessly positive approach, e.g. ++ ++ ++ +++ , is just the simplest form of a happy ending pattern. I’ve done well, you can expect more of the same in the future. The same can be said of the more sophisticated double reversal pattern, e.g. +++ — ++++, I suggested.

And what about a cliff hanger? A bit risky. You could end your grant proposal by listing some unsolved problems. But don’t make them sound so hard that the granting authorities, in spite of your past successes, don’t believe you capable of solving them.

Or, for your promotion interview, you could mention serious future challenges you’re well equipped to meet. Again, don’t overdo it.

**Tune in next post**

Well we’ve certainly done well haven’t we. We started from a simple boy-meets-girl trope and developed a whole calculus of plots. And we’ve shown they can be applied to professional rhetoric, like grant proposals.

In a future post we’ll further extend the la-di-da calculus and take on even more impressive proposals, like venture capital funding pitches. You’ll be up to your ankles in money!

Oh wait, I just realized something potentially catastrophic! I haven’t even tried any of this out! It’s completely untested! It could not work at all and leave you unfunded, unbonused and unpromoted!!

Fortunately I have an idea … but look at the word count. Just enough to tell you the key point, namely that

[WORD COUNT EXCEEDED POST TERMINATED]

]]>