APL each operator ¨: shape-preserving elementwise mapping without loops
The each operator ¨ lets you apply a function to every element of an APL array while keeping the original shape, avoiding explicit loops.
28 Aug 2025, 15:20 UTC

When you need to run a function that cannot be vectorized over every cell of an APL array, the temptation is to reach for explicit iteration. The each operator ¨ gives you a shape‑preserving alternative: apply a function to every element and keep the original structure intact.
The useful takeaway is that ¨ is a mapping combinator, not a loop. It descends into an array, applies the left operand to each element, and returns an array with the same shape.
What each actually does
In APL, each is written ¨. The left operand is a function, the right operand is an array. The expression f¨a applies f to each element of a independently.
The operator works with primitive functions, user‑defined dfns { } and tradfns. It preserves shape by construction, so a vector stays a vector, a matrix stays a matrix, and a nested array keeps its nesting.
For nested arrays, each descends to the deepest level unless you change its rank with the rank operator or depth operators. That makes it convenient for element‑wise transformations without writing explicit recursion.
Behavior is independent of index origin ⎕IO and migration level ⎕ML. The same expression gives the same result across settings and implementations.
When each shines and where it gets tricky
Each shines when the per‑element work is not expressible as a vectorized primitive. Parsing a string, calling a small helper dfn, or applying a conditional transformation are typical cases.
It composes well with other operators. You can combine ¨ with reduce /, rank, or power ⍣ to build more complex expressions while keeping the mapping explicit.
The main trade‑offs are memory and rank surprises. Each materializes a full result array of the same shape as the input. On very large arrays this can create significant memory pressure.
Confusing each with rank can produce unexpected results on higher‑rank arrays. If you need to apply a function to whole sub‑arrays rather than scalars, specify the desired rank explicitly instead of relying on the default scalar descent.
A concrete worked example you can check
Run the following in any APL session. No special permissions are required.
addTen ← {⍵ + 10}
v ← 1 2 3
m ← 2 3 ⍴ ⍳6
n ← (1 2) (3 4 5)
Apply each:
addTen¨v
addTen¨m
addTen¨n
Verify shape preservation:
⍴addTen¨v ≡ ⍴v
⍴addTen¨m ≡ ⍴m
⍴addTen¨n ≡ ⍴n
Each check should be 1. You can also inspect a few elements with ⎕← or ⎕DM to confirm the value is the original element plus ten.
Confirm independence from ⎕IO and ⎕ML:
⎕IO ← 0
r0 ← addTen¨v
⎕IO ← 1
r1 ← addTen¨v
⎕ML ← 0
rML0 ← addTen¨v
⎕ML ← 1
rML1 ← addTen¨v
⎕ML ← 2
rML2 ← addTen¨v
Compare the results. They should be identical across the different ⎕IO and ⎕ML settings, demonstrating that each does not depend on indexing or migration level.
Trade‑off: memory and rank
Because each builds a complete result array, applying it to a very large array can exhaust memory. If you only need a partial result or a streaming reduction, consider reduce or a rank‑limited form.
When working with higher‑rank data, be explicit about rank. Without it, each will descend to scalars. If you intended to map over rows or columns, use the rank operator to state the intended rank and avoid silent shape changes.
Check it in your environment
Pick a small representative array with known values, define a simple dfn, apply f¨a, and assert shape equality with ⍴. Repeat after changing ⎕IO and ⎕ML to confirm stable behavior. For large inputs, test on a reduced sample first to gauge memory use before scaling up.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.