Oleksandr Kaleniuk

@okaleniuk

APL deserves its renaissance too

There is an interactive (and slightly better) version of this article on wordsandbuttons.online: http://wordsandbuttons.online/apl_deserves_its_renaissance_too.html

This is a Game of Life in APL:life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}

I know, I know. I should have started with introduction, but doesn’t it introduce itself rather well? You can see for yourself that it’s ultimately concise, expressive and completely alien to all the mainstream computer languages.

In fact, it didn’t originate as a computer language at all. It was proposed as a better notation for tensor algebra by Harvard mathematician Kenneth E. Iverson. It was meant to be written by hand on a blackboard to transfer mathematic ideas from one person to another.

But due to its formality it turned up to be surprisingly good to transfer ideas from people to computers as well. It was made into a computer language in early 60-th, and these weird symbols like or were not a problem at all, because every manufacturer had its own char-set at the time anyway. ASCII wasn’t yet even ratificated.

Its popularity grown through the following years peaking in the 70th and decaying afterwards with the rise of BASIC based personal computing and C powered UNIX platform. It is still used in some niches, such as in financial sector, meaning people actually make money using APL up to this day. But what’s fascinating, the very first portable computer by IBM — IBM 5100 “a 50-lb package of interactive personal computing” — came 6 years before the IBM PC and with APL on board.

By Marcin Wichary from San Francisco, U.S.A. ([1] Uploaded by Partyzan_XXI) [CC BY 2.0 (http://creativecommons.org/licenses/by/2.0)], via Wikimedia Commons

The secret of APL’s popularity is simple: learning all the alien symbols is one time investment, and expressiveness — the leverage you as programmer has over the computation — is for life.

And it’s not even that hard to learn the language in the first place. You might not believe it, but it’s one of the simplest languages ever. Here, let me show you how the Game of Life works.

The left arrow is an assignment function and the brackets mark the function body. So this: life←{...}is simply a function definition.

In APL function arguments are tacit, meaning you don’t have to specify a name for every argument, you just know by convention, that the left argument is always ⍺ and the right is ⍵. Doesn’t it mean that APL functions take at most two arguments? Not really. When you want to call C-like function like this: foo(x, y, z), in APL terms you simply pass a tuple of 3 values. It’s still one ⍵ though.

Let’s run our life with some input. Let it be a planner. We’ll use a function to form a matrix out of linear array.

in ← 5 5 ⍴ 0 0 0 0 0  0 0 1 0 0  0 0 0 1 0  0 1 1 1 0  0 0 0 0 0
in
0 0 0 0 0
0 0 1 0 0
0 0 0 1 0
0 1 1 1 0
0 0 0 0 0

Running life for in will result in this:

life in
0 0 0 0 0
0 0 0 0 0
0 1 0 1 0
0 0 1 1 0
0 0 1 0 0

The planner moved!

In APL what we would call operators are functions too. Things like +, -, * etc. The functions are executed from right to left one at the time. There is no precedence, all the functions are equal.

The first function of the life body would be enclose: . What it does — it makes our 5x5 matrix input into a scalar containing 5x5 matrix.

⊂ in
┌─────────┐
│0 0 0 0 0│
│0 0 1 0 0│
│0 0 0 1 0│
│0 1 1 1 0│
│0 0 0 0 0│
└─────────┘

The next one is a bit trickier. The next function is rotate: . It rotates an array at given index.

1 ⌽ 1 2 3
2 3 1
0 ⌽ 1 2 3
1 2 3
¯1 ⌽ 1 2 3
3 1 2

But it doesn’t go by itself. It is itself an argument for an outer product operator: ∘.(in APL functions that take functions as arguments are called operators).

And together they do this:

¯1 0 1∘.⌽⊂in
┌─────────┬─────────┬─────────┐
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│
│0 0 0 0 1│0 0 0 1 0│0 0 1 0 0│
│0 0 1 1 1│0 1 1 1 0│1 1 1 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
└─────────┴─────────┴─────────┘

The next function also goes with an operator. It’s rotate first: . It works pretty much like rotate, but it rotates a nested array around the first level of “nestedness”.

1 ⊖ in
0 0 1 0 0
0 0 0 1 0
0 1 1 1 0
0 0 0 0 0
0 0 0 0 0
0 ⊖ in
0 0 0 0 0
0 0 1 0 0
0 0 0 1 0
0 1 1 1 0
0 0 0 0 0
¯1 ⊖ in
0 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 1 0
0 1 1 1 0

With outer product operator and our previous result it goes like this:

¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
┌─────────┬─────────┬─────────┐
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│
│0 0 0 0 1│0 0 0 1 0│0 0 1 0 0│
│0 0 1 1 1│0 1 1 1 0│1 1 1 0 0│
├─────────┼─────────┼─────────┤
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│
│0 0 0 0 1│0 0 0 1 0│0 0 1 0 0│
│0 0 1 1 1│0 1 1 1 0│1 1 1 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
├─────────┼─────────┼─────────┤
│0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│
│0 0 0 0 1│0 0 0 1 0│0 0 1 0 0│
│0 0 1 1 1│0 1 1 1 0│1 1 1 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
│0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│
└─────────┴─────────┴─────────┘

The next function in called ravel, and it does look like coma. What it does, it makes nested array into 1-dimensional.

, in
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0

It doesn’t ravel scalar, so being applied to our matrix of scalars containing matrices, it would make an array of 9 enclosed matrices.

The next one is again operator and function pair. Operator reduce: /, and a function plus: +. As you might guess, it reduces a summation of all matrices in the array:

+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
┌─────────┐
│0 1 1 1 0│
│0 1 2 2 1│
│1 3 5 4 2│
│1 2 4 3 2│
│1 2 3 2 1│
└─────────┘

Operator compare: = produces matrices of 0 and 1 based on whether every element in the right argument equals or not an every argument in the left argument. In our case this would result in:

3 4 = +/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
┌─────────┬─────────┐
│0 0 0 0 0│0 0 0 0 0│
│0 0 0 0 0│0 0 0 0 0│
│0 1 0 0 0│0 0 0 1 0│
│0 0 0 1 0│0 0 1 0 0│
│0 0 1 0 0│0 0 0 0 0│
└─────────┴─────────┘

Then there is logical part. Functions or: and and: used with the inner product . operator. With our input it results in an enclosed matrix where every 1 element of original input results in 1 only having 2 original neighbors, but also every element at all results in 1 having 3 neighbors nevertheless was it set to 1 in original input or not.

Which is basically the rules for the Game of Life.

1 in∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
┌─────────┐
│0 0 0 0 0│
│0 0 0 0 0│
│0 1 0 1 0│
│0 0 1 1 0│
│0 0 1 0 0│
└─────────┘

And the last function mix here simply removes enclosure.

↑1 in∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
0 0 0 0 0
0 0 0 0 0
0 1 0 1 0
0 0 1 1 0
0 0 1 0 0

Not at all alien now, is it?

By User:Rursus (APL-keybd.svg) [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/)], via Wikimedia Commons

But why it deserves its renaissance after all?

It was designed to be written by hand. However for decades now it was only typed in with the keyboard. And the weird one, not the one you could just buy in any store. APL however has its ASCII friendly descendants inherited its expressiveness and concision, but frankly they are all ugly beyond the possibility of public success. It’s not that APL is alien to computers, it’s just computers were alien to APL for quite a while.

But now, with the development of touch interfaces and optical character recognition, it might just get its second chance. I would personally prefer drawing several rows of APL characters on a tablet to typing hundreds of lines in Python with a virtual keyboard.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Oleksandr Kaleniuk

Topics of interest

More Related Stories