McCarthy is credited with observing that in a programming language it is useful to require that a boolean operator between two predicate expressions not evaluate the second operand if the operator result is determined by the value of the first. Thus the C expression “(p && p−> field)” is safe even when the pointer p is NULL, for then the indeterminate side effects of fetching the field are suppressed.

This pattern sometimes grows gracefully to more complex cases. After such growth the resulting boolean orgy may need to grow further where the pattern does not fit, and then a dilemma arises.

We want to preserve code that has been proven by many rereadings and operation in the field.

A physicist may transform “a*(b+c)” into “a*b + a*c”, thinking only as a mathematician, not a physicist. He need not think about the physical meaning of “a*b” to make this transformation. I set out to consider such transformations of these boolean orgies in order to transform the code reliably. Here are some of the results.

Where we may write “x = P ? a : b;” or “if(P) x = a; else x = b;” in C, we would write “x := (P|a|b)” in Algol68. This avoids C’s dangling else problem that I find immensely confusing and I shall thus use this notation since the semantics are the same.

What becomes of “(P or (Q and R) | a | b)” if we need McCarthy semantics and the languages does not provide them?
First we must rewrite (P or Q | a | b) as (P | a |(Q | a | b)). Note the repetition of code “a” that is introduced by this transformation. In this sort of situation I agree with Dijkstra that goto is bad. I also think that making a procedure of “a” can be as confusing, and C’s lack of nested procedure definitions make such transformations ugly, slow, immodular and error prone. gcc’s nested procedures ameliorate this but still the required routines may have no natural meaning in the application domain.

The code that provoked this note is more like
(P or (Q and R) | a | b) = (P | a | (Q |(R | a | b) | b)); the problem compounds, or at least accumulates.

Sometimes the predicate expressions have natural side effects that are necessary to the consequent imperatives−if P is false then the evaluation of R may be necessary for the execution of a, in the above example. If we find a new action X that must be performed after computing that Q = false, but before imperative b, where does X go? Putting X as the head of b may conflict with other entries to b. One way is (P or ((Q or X) and R) | a | b). It must be contrived that X yield false and the Algol68 construct “(X; false)” will do the trick. In gcc ({X; 0}) does too. Often (X, 0) will work in C.

Translating this exercise to C yields:
if(P || ((Q || (X, 0)) && R)) a; else b;
Concise but unconventional!

I do not like the above but what are the alternatives?


I began this note with the aim of burying this style. There are yet more tricks here to explore first however.

First note that the leaf acts, as I shall call them, can be applied to either branch of the conditional. If X must be performed after P is found to be so, but Y is required if P is not so then we may do:
((P and (X; true)) or (Y; false)|a|b)
In this example the leaf is too close to the stem to be of interest, for X and Y could have been moved into a and b. The pattern is useful when other leaves do not need X or Y.

While side effects and McCarthy’s semantics break commutativity of the Boolean operators, they do not break either form of associativity. To wit:
A and (B and C) = (A and B) and C
A or (B or C) = (A or B) or C

If only A is true then distributivity is broken for A is performed once in the first form but twice in the second case below:
A and (B or C) = (A and B) or (A and C)
(A or B) and C = (A and C) or (B and C)
The same goes when and and or are interchanged.

This can be used to share code when two but not all contingencies require some preact:
((P and (X; true)) or (Q and (X; true))|a|b)
can be shortened to
((P or Q) and (X; true)|a|b)

This leads to perhaps the ultimate step; we can do it all with leaf acts and entirely omit the imperatives!
(P|a|b) = P and (a; true) or (b; false)
or when we care not about the value of the statement merely:
(P|a|b) = P and a or b

The routine verify in this Scheme application uses this style along with continuations.


Who knows what lurks in the minds of programmers?
The compiler knows!