APL and syntax
When mentioning APL to someone who has never used it, the reaction is often along the lines of “isn't that the unreadable language with weird symbols?”.
The classic example of APL code is Conway's Game of Life which can be written as such:
{⊃1 ⍵ ∨.∧ 3 4 = +/ +⌿ ¯1 0 1 ∘.⊖ ¯1 0 1 ⌽¨ ⊂⍵}
This implies that the answer to the question above is yes, but there is an implication in that statement that suggests that the symbols is what makes the language different.
Another language in the same family as APL is J. This language uses many of the same principles as APL, and Game of Life in J looks like this:
n1dim=:3 +/\ (0,,&0)
neighbors=: -~ [: n1dim"1 n1dim
life=: [: (0.5 >: 3 |@- ]) -:+neighbors
For someone without knowledge of either APL nor J, both of these examples are probably inscrutable. The point of the example above is to emphasise that it's not the symbols that makes APL seem alien to a newcomer.
The question then becomes: Why does APL look the way it does?
To answer this question, we need to look into the history of the language and the requirements that led to its design.
The designer of both APL and J was Ken Iverson. He was a mathematician, and looked at problem solving from a mathematical perspective. APL was originally a notation for array manipulation, and it was only later that it was actually implemented as a programming language for computers.
Thus, to understand the APL notation, it's important to think about it as a concise way to describe mathematical operations, especially operations working on arrays of values.
The first step is to look at the syntax of maths, not programming languages. Let's take a simple example of summing all the elements of an array \(b\):
\[ \sum_i b_i \]
The above syntax is a bit imprecise and we are assuming that \(i\) will take on all the valid indexes in \(b\).
If we want to take the product of all the elements in \(b\), then we can replace the summation sign with the product symbol:
\[ \prod_i b_i \]
What if we want to create a chain of exponentiations? There is no dedicated symbol for this, so we need to write:
\[ b_0^{b_1^{b_2^{\cdot^{\cdot^{\cdot^{b_{n-1}}}}}}} \]
In the example above, we're assuming that the reader will understand that \(n\) represents the number of elements in the array.
The observation here is that these operations are all variations of the same underlying operation. In functional programming this is usually referred to as a reduction.
To perform the sum in APL, one wrould write the following:
+/b
The +
is the operation to perform, and /
is the reduction operator. The natural extension to the product is:
×/b
And for exponentiation:
*/b
The argument in favour of APL notation is that it's simpler and more concise than traditional mathematical notation. That's no short order, as maths is already supposed to be both simple and concise. Not only that, the APL notation is free of imprecision as opposed to the mathematical examples, as was discussed above.
The APL syntax also allows you to use any other function as part of a reduction. For example, the function ⌊
returns the smaller of the two values, so the expression ⌊/b
can be used to find the smallest value in \(b\). The way to write this in mathematical notation is as follows:
\[ \min_i(b_i) \]
It is assumed that \(\min\) is well known, since in APL we make the same assumption concerning ⌊
.
The reason why APL syntax looks the way it does, is because it's a mathematical notation that happens to be suitable for evaluation by a computer, rather than a set of instructions for a computer to perform.