Beginner

Operators in APL

Learn APL operators - arithmetic, comparison, logical, scalar extension, reduction, and right-to-left evaluation with practical Docker-ready examples

In most languages, operators apply to single values, and you write loops to apply them across collections. APL turns this idea inside out: every primitive operator applies to entire arrays without any loop syntax at all. Adding two scalars and adding two thousand-element vectors use exactly the same +.

This tutorial walks through APL’s arithmetic, comparison, logical, and reduction operators, and introduces two ideas that make APL’s operator model distinctive: scalar extension (a scalar automatically pairs with every element of an array) and right-to-left evaluation with equal precedence (no PEMDAS — the parse is uniform).

APL distinguishes between functions (primitives like +, ×, ) and operators in the strict sense (higher-order modifiers like / reduce and \ scan that take functions as arguments). In casual usage we call them all “operators,” but it’s worth knowing the difference: + is a function, while +/ is the reduce operator applied to the + function to produce a sum.

Arithmetic Operators

APL’s arithmetic primitives use traditional mathematical symbols, not the ASCII compromises most languages settle for. Multiplication is × (not *), division is ÷ (not /), and * is reserved for exponentiation — which matches how mathematicians write it.

Create a file named arithmetic.apl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
⍝ Scalar arithmetic
2 + 3
10 - 4
3 × 5
20 ÷ 4
2 * 10
5 | 17

⍝ Scalar extension: a scalar pairs with every element
10 + 1 2 3 4 5
2 × 1 2 3 4 5

⍝ Element-wise with two arrays of the same shape
1 2 3 + 10 20 30

⍝ Negation and reciprocal (monadic forms)
- 7
÷ 4
)OFF

A few things to notice here. 5 | 17 is the residue function: the right argument divided by the left, taking the remainder — so 17 mod 5 = 2. The order is the reverse of what C-family programmers expect. 2 * 10 is 2 raised to the 10th power, not 2 times 10. And - and ÷ are both monadic (one-argument) and dyadic (two-argument): - 7 negates, ÷ 4 produces the reciprocal 0.25.

Comparison and Logical Operators

Comparisons in APL return 1 for true and 0 for false — there is no separate boolean type. This means the result of a comparison is itself a numeric array you can feed back into arithmetic, which is the foundation of most “branchless” APL idioms.

Create a file named compare.apl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
⍝ Scalar comparisons return 0 or 1
5 > 3
3 = 3
7  7

⍝ Comparison extends across arrays
1 2 3 4 5 > 3
1 2 3 4 5 = 3

⍝ Logical operators on booleans
1  0
1  0
~ 1

⍝ Count how many elements satisfy a condition
+/ 1 2 3 4 5 > 2
)OFF

The last line is a classic APL idiom worth pausing on. 1 2 3 4 5 > 2 produces the boolean vector 0 0 1 1 1, and +/ sums it. In one line, with no if statement and no loop, we counted the elements greater than 2. This pattern — generate a boolean mask, then reduce — replaces an enormous amount of imperative code.

Reduction and Scan

The / operator (reduce) inserts a function between every element of an array. +/ 1 2 3 4 5 is conceptually 1 + 2 + 3 + 4 + 5. The \ operator (scan) does the same thing but keeps every intermediate result, giving you a running total, running max, or running anything-else for free.

Create a file named reductions.apl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
⍝ Reduce: collapse a vector to a single value
+/ 1 2 3 4 5
×/ 1 2 3 4 5
/ 3 1 4 1 5 9 2 6
/ 3 1 4 1 5 9 2 6

⍝ Scan: keep every intermediate result
+\ 1 2 3 4 5
×\ 1 2 3 4 5
\ 3 1 4 1 5 9 2 6

⍝ Combining: average of a vector
(+/ 2 4 6 8 10) ÷  2 4 6 8 10
)OFF

and are dyadic max and min — 3 ⌈ 7 is 7. Combined with reduce, ⌈/ finds the largest element and ⌊/ the smallest. The final expression shows the canonical APL “mean”: sum the vector, divide by its length ( returns the shape, here 5).

Right-to-Left Evaluation

APL has no operator precedence. Every function — +, ×, *, your own user-defined ones — has identical priority, and expressions are parsed strictly right-to-left. This sounds disruptive but is actually liberating: you never need a precedence table, and pipelines read as a chain of transformations.

Create a file named precedence.apl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
⍝ Right-to-left: 3 + 4 evaluates first, then 2 ×
2 × 3 + 4

⍝ Contrast with most languages where × binds tighter
⍝ APL gives 14; C/Python/Java would give 10

⍝ Parentheses override grouping
(2 × 3) + 4

⍝ A longer chain reads right-to-left
2 + 3 × 4 - 1

⍝ The same chain made explicit with parens
2 + (3 × (4 - 1))
)OFF

The pair 2 × 3 + 4 and its right-to-left interpretation 2 × (3 + 4) = 14 is the single most common stumbling block for APL newcomers. Once you accept the rule, it stops being surprising — and it means you write fewer parentheses than in almost any other language.

Running with Docker

1
2
3
4
5
6
7
8
# Pull the GNU APL image
docker pull juergensauermann/gnu-apl-1.8

# Run each example
docker run --rm -v $(pwd):/app -w /app juergensauermann/gnu-apl-1.8 apl --silent --noColor --noCIN -f arithmetic.apl
docker run --rm -v $(pwd):/app -w /app juergensauermann/gnu-apl-1.8 apl --silent --noColor --noCIN -f compare.apl
docker run --rm -v $(pwd):/app -w /app juergensauermann/gnu-apl-1.8 apl --silent --noColor --noCIN -f reductions.apl
docker run --rm -v $(pwd):/app -w /app juergensauermann/gnu-apl-1.8 apl --silent --noColor --noCIN -f precedence.apl

Expected Output

Output from arithmetic.apl:

5
6
15
5
1024
2
11 12 13 14 15
2 4 6 8 10
11 22 33
¯7
0.25

Output from compare.apl:

1
1
0
0 0 0 1 1
0 0 1 0 0
0
1
0
3

Output from reductions.apl:

15
120
9
1
1 3 6 10 15
1 2 6 24 120
3 3 4 4 5 9 9 9
6

Output from precedence.apl:

14
10
11
11

Note: APL uses the high minus sign ¯ (not the regular -) to prefix negative number values, distinguishing them from the subtraction function. So monadic negation of 7 produces ¯7 in the output.

Key Concepts

  • Scalar extension: a scalar operand pairs with every element of an array operand, so 10 + 1 2 3 is 11 12 13. No broadcasting rules to memorize — every primitive does it.
  • Functions vs operators: primitives like + and × are functions; / and \ are true operators (higher-order) that take a function and produce a derived function like +/ (sum) or +\ (running sum).
  • Booleans are numbers: comparisons return 0 or 1 integers you can immediately sum, multiply, or use as a mask. +/ vec > threshold counts; (vec > threshold) / vec filters.
  • Right-to-left, equal precedence: there is no PEMDAS. 2 × 3 + 4 is 14. Use parentheses when you want non-default grouping; you’ll need them less often than you expect.
  • Monadic and dyadic forms: most primitive symbols mean two different things depending on whether they have one argument (- 7 → negate) or two (10 - 7 → subtract).
  • Residue order is reversed: A | B is B mod A, not A mod B. This is consistent with APL’s “left argument is the parameter, right is the data” convention but trips up newcomers.
  • High minus ¯ is not subtraction: ¯7 is the literal value negative seven; - 7 is the subtraction function applied monadically. They produce the same value but mean different things syntactically.

Running Today

All examples can be run using Docker:

docker pull juergensauermann/gnu-apl-1.8:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining