Let me show you characteristics of APL that will influence your understanding of programming concepts and the way you use other languages.

Alan Perlis, the computer scientist recipient of the first Turing award, wrote
“A language that doesn’t affect the way you think about programming, is not worth knowing.”
― Alan J. Perlis, 1982. Special feature: Epigrams on programming. ACM Sigplan Notices, 17(9), pp.7-13.
Inspired by this statement, this article focuses on showing you how a language that does affect the way you think is a language worth knowing.
In particular, I will be drawing from my own experiences with APL and Python to give empirical evidence that supports my statement.
This article is also the written version of a talk I gave at FnConf 2022.
The talk slides are available here and I’ll link to the YouTube recording ASAP.
Background
I started writing Python code in 2012 and that is the programming language I am most fluent in.
Before Python, I had written code in other languages such as Visual Basic, JavaScript, and C,
and after picking up Python I played around with many other different languages
such as Pascal, Haskell, C++, F#, Matlab, Mathematica, and Java.
Then, in 2020, I started learning APL.
Today, I am nowhere nearly as competent with APL as I am with Python, and yet,
my experience with APL has notably affected the way I think about programming,
the way I understand and reason about certain computer science concepts,
and especially the way I write my Python code.
So, in short, in this article I will share evidence that supports the statement that
“APL is a language worth knowing because it affects the way you think about programming.”
As you will soon find out, APL has had such a notorious impact in my programming because APL is the programming language that is most different from everything else I have learned.
Therefore, it is only natural that APL has been the language that provided more enlightening moments for me.
Disclaimer
Of course, all evidence I am sharing is drawn from my own programming experience and is subjective.
Thus, my examples should not be taken as definite proof that (Python) programmers will get the same benefits as I did from learning APL.
I just think that this following scenario does not make much sense:
― [someone] So, Rodrigo, how has your experience with APL been?
― [me] It’s been great! It even has influenced my code in other languages, for example in Python.
― [someone] Really? How so?
― [me] I don’t know, I just feel it.
Because of that, I spent a lot of time reflecting and trying to come up with concrete and objective instances of things that were influenced by APL.
That ought to make for a more compelling conversation than the hypothetical scenario from above.
Prerequisites
While I will be talking about APL a lot, I will not assume that you know APL:
I will explain just about enough for you to follow along.
You also don’t need to know Python, but you are expected to be familiar with one imperative programming language.
I need you to be comfortable with variables, conditional statements, and loops.
In short, APL is an array-oriented programming language whose natural and concise syntax lets you develop shorter programs while thinking more about the problem you are trying to solve than how to express it to a computer.
The brief description that follows introduces you to just about enough APL for you to be able to follow the remainder of the article.
Take your time to digest what you are reading, as some of these things may surprise you!
Also, for the sake of simplicity, some of the things written here may not be 100% accurate.
Finally, feel free to open the Try APL website and try the examples there.
APL is an interpreted language, and the typical environment in which you use it is the session,
which is very similar to REPLs from other languages.
When showing APL code, the APL code will be indented to the right and the result(s) will be aligned on the left,
like so:
15 + 16
31
15 - 14
1
APL is infamous for its Unicode symbols, which are actually the set of its built-ins.
For example, many languages have a built-in that lets you create a series of consecutive integers.
In Python, that would be range
.
In APL, we have the built-in function index generator, represented by the symbol iota ⍳
:
⍳6
0 1 2 3 4 5
Being an array-oriented language, APL has built-in support for arrays of multiple dimensions.
Single numbers are scalars, like the 15
and the 16
in 15 + 16
,
and a linear succession of scalars is a vector, like in 0 1 2 3 4 5
.
Finally, studying the expression 10 - 5 - 2
will highlight another key difference between APL and most other languages.
What should be the result of that expression?
If you follow the rules of traditional mathematics the result is (3):
[
10 – 5 – 2 = (10 – 5) – 2 = 5 – 2 = 3]
However, in APL, we start evaluating from right to left,
meaning 10 - 5 - 2
is equivalent to 10 - (5 - 2)
and the result is 7
:
10 - (5 - 2)
7
10 - 5 - 2
7
I want to start by talking about list comprehensions,
the syntactic constructs (available in some programming languages) that make it easier to create new lists based on existing lists.
I want to show you how APL made it easier for me to understand list comprehensions and to use them effectively.
Scalar functions in APL
In APL, everything is an array and all arrays are composed of scalars,
which ties with the fact that in APL many of its built-ins are said to be scalar.
What that means is that they “ignore” the structure of the array argument(s) and operate directly on the scalars.
Take a look at these examples:
⍳6 ⍝ 6 integers starting from the origin.
0 1 2 3 4 5
1+⍳6 ⍝ Add 1 to the 6 integers starting from the origin.
1 2 3 4 5 6
2×⍳6 ⍝ Multiply by 2 the 6 integers starting from the origin.
0 2 4 6 8 10
Notice how:
-
1+
does not care that⍳6
is a vector and just adds 1 to each of the scalars in⍳6
; and -
2×
does not care that⍳6
is a vector and just doubles each of the scalars in⍳6
.
The fact that many functions are scalar is extremely important
because it makes it convenient to express several computations that apply to your whole data.
This contrasts with the need of doing the computations one scalar at a time,
like in many other languages.
We’ve seen a dyadic scalar function with a scalar on the left and a vector on the right:
10 + 0 1 2 3 4 5
10 11 12 13 14 15
A dyadic function is a function that accepts two arguments.
A dyadic scalar function can also take a vector on the left and a scalar on the right:
0 1 2 3 4 5 + 10
10 11 12 13 14 15
Neither argument has to be a scalar, though.
As long as their shapes are compatible, both the left and right argument can be other arrays.
For example, both arguments can be vectors, as long as they have the same length:
100 0 1 × 2 3 4
200 0 4
Much like ×
is the symbol that APL uses for multiplication,
APL uses ÷
to represent division.
Knowing that, determine the result of the next expression:
24 ÷ 12 6 -4 2
?
Before I show you the actual result,
think about the results of the expressions that follow:
24÷12 6-4 2
?
24 ÷ 12 6 - 4 2
?
All three expressions above evaluate to 3 6
.
Why?
Because the minus sign -
was always used as the dyadic function minus.
Thus, the expression 24 ÷ 12 6 -4 2
starts by evaluating 12 6 -4 2
:
12 6 -4 2
8 4
Then, we do the division 24 ÷ 8 4
:
3 6
The example above was crafted purposefully with the intent of making you think that the -
next to the 4
would make the number “negative four”, which would challenge the simplicity that APL aims for.
As you can see, negative numbers are not typed with a -
next to them, but with a high minus:
1 - 3
¯2
24 ÷ 12 8 ¯4 2
2 3 ¯6 12
A couple of other useful scalar functions include the function power *
, which other languages often represent by **
, pow
, power
, etc:
1 2 3*2
1 4 9
2*⍳11
1 2 4 8 16 32 64 128 256 512 1024
And the function residue |
, which other languages represent by %
, mod
, etc:
2|⍳5
0 1 0 1 0
10|1 12 123 1234
1 2 3 4
In APL, the modulus goes on the left of the residue function,
which is the opposite of many other languages do (including Python).
Python’s n % m
is equivalent to APL’s m|n
.
List comprehensions express data transformations
List comprehensions are a syntactical construct of some programming languages whose objective is to make it easier for you to create new lists (or similar structures) out of existing lists (or similar structures).
To work with an example, suppose we want to square the integers from 0 to 9.
How would we do that?
The typical Python solution would look like this:
>>> squares = []
>>> for num in range(10):
... squares.append(num ** 2)
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
The structure of the code we just wrote is very clear:
- we create an empty result list;
- we go over an existing list (in this case, a
range
); and - we add the modified value to the result list.
The objective of list comprehensions is to take this pattern and