Skip to content

What’s Wrong with Parentheses?

Vector 7:2, page 125

programming techniques development practice

Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 125

Abstract

There is a tendency to avoid the use of parentheses in APL. Some renowned specialists even claim they never use them. Excluding parentheses however may, in a lot of cases, decrease readability and as such reduce maintainability. In some cases, the use of unnecessary parentheses even increases readability. So, "What’s wrong with parentheses?".

In Vector Vol.5 No.3 page 31, Alan Sykes (1) presented the defined function COUNTALL which returns as explicit result R the frequency table of the right argument vector X, unique values being placed in ascending order within the first column:

    ∇TAB←COUNTALL X;I;N
[1]   X←X[⍋X] ⋄ R← 1 2 ⍴(1↑X),⍴X
[2]   →(X[1]=X[⍴X])/0
[3]   I←(1⌽X)≠X ⋄ N←I/⍳⍴I
[4]   TAB←(I/X),[1.5]N-0,¯1↓N
    ∇

In Vector Vol.6 No.2 page 23, Gerard Langlet (2) establishes this function works perfectly except when X is not a vector, index origin is set to 0, or X is an empty vector (array). He introduces the following version COUNTALLV:

    ∇R←COUNTALLV X
[1]   X←,X ⋄ R←¯1+1↑X←X[⍋X] ⋄ R←X≠1⌽X←R,X ⋄ X←1↓R/X ⋄ R←X,[⎕IO+.5]1↓R-¯1⌽R←R/⍳⍴R
    ∇

which runs correctly for any numeric array in either origin ⎕IO.

Both functions still produce DOMAIN ERROR when X is a character vector (or array) (?).

It is true that COUNTALLV is index origin independent, but for numeric arrays it is still - just like COUNTALL - comparison tolerance dependent. Why are we so anxious to make defined functions and eventually defined operators index origin independent, while nobody cares about comparison tolerance dependency?

The statement that both functions do not work for character vectors (or arrays) however, is only partly true.

In VAX APL and Dyalog APL e.g., the monadic form of grade up and grade down of character vectors and matrices/arrays is supported, the collating sequence being implied by the ordering in the atomic vector ⎕AV. In WATCOM APL too, the monadic form of grade up and grade down of character vectors and matrices is supported, but the collating sequence is determined by the system variable ⎕CS (the default value being ⎕AV).

This means that with such implementations, the function COUNTALL works perfectly for non-empty character strings and index origin 1, while COUNTALLV doesn’t work at all: The statement

R←¯1+1↑X←X[⍋X]

contains a mishmash of types and causes a DOMAIN ERROR.

But what disturbs me at most is: What are we talking about? Are we talking about teaching of and with APL? Or are we talking about the use of ingenious but sometimes irritating tricks which in most cases decrease readability and as such reduce maintainability?

When in my courses I tackle the problem of producing such a frequency table, I first describe how we would do it with paper and pencil: (1) sorting of the data in ascending order; (2) eliminating of the duplications in the ordered set; and (3) counting the frequency of the elements of the cleaned set in the original but ordered set. Translating this algorithm literally into APL gives the defined function COUNTALL1:

    ∇ R←COUNTALL1 X
[1]   X←X[⍋X←,X]
[2]   R←((X⍳X)=⍳⍴X)/X
[3]   R←R,[⎕IO+0.5]+/R∘.=X
    ∇

which runs correctly for any numeric or alphabetic array in either origin ⎕IO. In addition, it is an impressive demonstration of the beauty and power of the outer product generator. That the use of the outer product may be avoided is not relevant - at that stage of the course at least. Most important is that, in comparison with COUNTALL and COUNTALLV, the function COUNTALL1 is extremely clear and perfectly readable.

Of course we may avoid the use of parentheses, as is done e.g. in the defined function COUNTALL2:

    ∇ R←COUNTALL2 X
[1]   X←X[⍋X←,X]
[2]   R←X⍳X
[3]   R←R=⍳⍴X
[4]   R←R/X
[5]   R←R,[⎕IO+0.5]+/R∘.=X
    ∇

and we may write the sequence of statements on one line, using the diamond separator, which results in the defined functions COUNTALL3 and COUNTALL4 respectively:

    ∇ R←COUNTALL3 X
[1]   X←X[⍋X←,X] ⋄ R←((X⍳X)=⍳⍴X)/X ⋄ R←R,[⎕IO+0.5]+/R∘.=X
    ∇

    ∇ R←COUNTALL2 X
[1]   X←X[⍋X←,X] ⋄ R←X⍳X ⋄ R←R=⍳⍴X ⋄ R←R/X ⋄ R←R,[⎕IO+0.5]+/R∘.=X
    ∇

But COUNTALL1 is much more readable than COUNTALL2 and when I try to decipher one-liners of statements separated by diamonds such as in COUNTALL3 and COUNTALL4, the first thing I do is - like most of my students - to write the separated statements beneath each other, from top to bottom. In addition, for this example, the advantage in memory space and cpu time of excluding parentheses and/or using the diamond separator is quasi negligible.

Another even more impressive example is the calculation of the length of the hypotenuse of a right triangle from its legs A and B. This may be done with the defined functions HYP1 or HYP2:

    ∇ R←A HYP1 B
[1]   R←((A*2)+(B*2))*0.5
    ∇

    ∇ R←A HYP2 B
[1]   R←((A*2)+B*2)*0.5
    ∇

… showing how, due to the so-called right-to-left rule, the second pair of internal parentheses is superfluous (Whether this improves readability is discussable!).

The functions may be used in the higher-dimension case by evaluating A HYPX B HYPX C … or - if the implementation supports the use of defined functions as arguments of operators - by evaluating HYPX/A,B,C,…. The extension to the higher-dimension case however, may be incorporated in the defined function itself such as is done for instance in the function HYP3:

    ∇ R←HYP3 V
[1]   R←(+/V*2)*0.5
    ∇

where the right argument vector V is the cocatenation of the legs A B C … (V←A,B,C,…).

Once again, the use of parentheses may be excluded as is done in the defined functions HYP4 and HYP5 respectively:

    ∇ R←A HYP4 B
[1]   R←A*2
[2]   R←R+B*2
[3]   R←R*0.5
    ∇

    ∇ R←HYP5 V
[1]   R←+/V*2
[2]   R←R*0.5
    ∇

and we may write the sequence of statements on one line, using the diamond separator, as is done in the defined functions HYP6 and HYP7 respectively:

    ∇ R←A HYP6 B
[1]   R←A*2 ⋄ R←R+B*2 ⋄ R←R*0.5
    ∇

    ∇ R←HYP7 V
[1]   R←+/V*2 ⋄ R←R*0.5
    ∇

But once again, the excluding of the parentheses and the use of the diamond separator decrease readability, while the difference in memory space and cpu time is quasi negligible. This does not mean that the diamond separator shouldn’t be supported by the ISO APL Standard: most current non-IBM implementations of APL support the diamond separator and a lot of publications and software made available over the past ten to fifteen years make use of the separator (Whether we like it or not!) (3).

In "APL: An Interactive Approach" (4), L. Gilman and A.J.Rose advise: "As you begin to exercise your skills building expressions with many functions, don’t hesitate to overparenthesise. When you are more at home in your understanding of the APL language, you can experiment with omitting, nonessential parentheses." More recently, in "APL2 at a Glance" (5), J.A.Brown et al. proclaim: "If parentheses help you understand what you write, use them." I should say: "If parentheses help you and the readers of your programs understand what you write, use them."

Opponents of APL claim that APL is not readable and as such is not maintainable. This is a myth. APL is one of the most readable and maintainable programming languages available. However, as a consequence of its conciseness and powerfulness, we may be tempted needlessly to complicate and obscure our programs. Please, let us keep our programs clear and lucid. So, "What’s wrong with parentheses?".

Note: In SHARP APL and APL90, using the lev function, the hypothenuse may be calculated through the single statement without parentheses:

R←R*0.5⊣R←+/V*2

But in my opinion, this too is much less readable than the statement:

R←(+/V*2)*0.5

References

[1] Sykes A. Counting in APL - Part Two. Vector, Vol.5, No.3, January 1989, pp.26-32.

[2] Langlet G.A.. APL "RISC" Programming Style. Vector, Vol.6, No.2, October 1989, pp.23-24.

[3] De Kerf J.L.F. A Note on Separators in APL. APL-CAM Journal, Vol.11, No. 3, 16 July 1989, pp.683-696. See also Vector, Vol.6, No.2, October 1989, pp.123-139.

[4] Gilman L. and Rose A.J. APL: An Interactive Approach - Third Edition. John Wiley and Sons, New York, New York, 1984.

[5] Brown J.A., Pakin S., Polivka R.P. APL2 at a Glance. Prentice Hall, Englewood Cliffs, New Jersey, 1988. See also: Brown J.A., Pakin S., Polivka R.P. APL2 - Ein erster Einblick. Springer-Verlag, Berlin-Heidelberg, 1989.