Python Equivalents for APL Functions
Table of Contents
- Tutorial: APL Functions Across Programming Languages
- Reverse
- Rotate
- Ravel
- Reshape
- Transpose
- Ceiling
- Floor
- Absolute Value
- Index Generator
- Reshape with Scalar
- Reduce
- Scan
- Outer Product
- First
- Take
- Drop
- Reverse First
- Membership
- Unique / Nub
- Grade Up
- Grade Down
- TODO Encode
- TODO Decode
- TODO Interval Index
- TODO Matrix Inverse
- Shape
- Plus Reduce (Sum)
- Multiply Reduce (Product)
- Minimum Reduce
- Maximum Reduce
- Index of First Match
- Thread
- Apply Transforms
- 2026 Review: APL in the Modern Array Programming Landscape
Tutorial: APL Functions Across Programming Languages
This tutorial explores essential APL functions and demonstrates their equivalent implementations in Python.
Reverse
APL
⌽ 1 2 3 4 5= → =5 4 3 2 1
Python
from typing import List, TypeVar T = TypeVar('T') def reverse(xs: List[T]) -> List[T]: return xs[::-1] print(reverse([1, 2, 3, 4, 5])) # [5, 4, 3, 2, 1]
Rotate
APL
2⌽ 1 2 3 4 5= → =3 4 5 1 2
Python
def rotate(n: int, xs: List[T]) -> List[T]: n = n % len(xs) return xs[n:] + xs[:n] print(rotate(2, [1, 2, 3, 4, 5])) # [3, 4, 5, 1, 2]
Ravel
APL
, 2 2 ⍴ 1 2 3 4= → =1 2 3 4
Python
from typing import List, Any def ravel(matrix: List[List[Any]]) -> List[Any]: return [item for row in matrix for item in row] print(ravel([[1, 2], [3, 4]])) # [1, 2, 3, 4]
Reshape
APL
3 2 ⍴ 1 2 3 4 5 6= → =(1 2)(3 4)(5 6)
Python
from typing import List, Any def reshape(shape: List[int], xs: List[Any]) -> List[List[Any]]: result = [] for i in range(shape[0]): result.append(xs[i*shape[1]:(i+1)*shape[1]]) return result print(reshape([3, 2], [1, 2, 3, 4, 5, 6])) # [[1, 2], [3, 4], [5, 6]]
Transpose
APL
⍉ 3 2 ⍴ 1 2 3 4 5 6= → =(1 3 5)(2 4 6)
Python
from typing import List, TypeVar T = TypeVar('T') def transpose(matrix: List[List[T]]) -> List[List[T]]: return list(map(list, zip(*matrix))) print(transpose([[1, 2], [3, 4], [5, 6]])) # [[1, 3, 5], [2, 4, 6]]
Ceiling
APL
⌈ 3.7 ¯1.1 0 5.1= → =4 ¯1 0 6
Python
import math from typing import List def ceiling(xs: List[float]) -> List[int]: return [math.ceil(x) for x in xs] print(ceiling([3.7, -1.1, 0, 5.1])) # [4, -1, 0, 6]
Floor
APL
⌊ 3.7 ¯1.1 0 5.1= → =3 ¯2 0 5
Python
import math from typing import List def floor(xs: List[float]) -> List[int]: return [math.floor(x) for x in xs] print(floor([3.7, -1.1, 0, 5.1])) # [3, -2, 0, 5]
Absolute Value
APL
| 1 ¯2 3 ¯4= → =1 2 3 4
Python
from typing import List def absolute(xs: List[float]) -> List[float]: return [abs(x) for x in xs] print(absolute([1, -2, 3, -4])) # [1, 2, 3, 4]
Index Generator
APL
⍳ 5= → =1 2 3 4 5
Python
from typing import List def index_gen(n: int) -> List[int]: return list(range(1, n+1)) print(index_gen(5)) # [1, 2, 3, 4, 5]
Reshape with Scalar
APL
5⍴2= → =2 2 2 2 2
Python
from typing import List, TypeVar T = TypeVar('T') def scalar_reshape(n: int, x: T) -> List[T]: return [x] * n print(scalar_reshape(5, 2)) # [2, 2, 2, 2, 2]
Reduce
APL
+/ 1 2 3 4 5= → =15
Python
import functools from typing import List, Callable, TypeVar T = TypeVar('T') def reduce(f: Callable[[T, T], T], xs: List[T]) -> T: return functools.reduce(f, xs) print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])) # 15
Scan
APL
+\ 1 2 3 4 5= → =1 3 6 10 15
Python
import itertools from typing import List, Callable, TypeVar T = TypeVar('T') def scan(f: Callable[[T, T], T], xs: List[T]) -> List[T]: return list(itertools.accumulate(xs, f)) print(scan(lambda x, y: x + y, [1, 2, 3, 4, 5])) # [1, 3, 6, 10, 15]
Outer Product
APL
1 2 3 ∘.× 10 20 30= → =(10 20 30)(20 40 60)(30 60 90)
Python
from typing import List, Callable, TypeVar T = TypeVar('T') def outer_product(f: Callable[[T, T], T], xs: List[T], ys: List[T]) -> List[List[T]]: return [[f(x, y) for y in ys] for x in xs] print(outer_product(lambda x, y: x * y, [1, 2, 3], [10, 20, 30])) # [[10, 20, 30], [20, 40, 60], [30, 60, 90]]
First
APL
⊃ 1 2 3 4 5= → =1
Python
from typing import List, TypeVar T = TypeVar('T') def first(xs: List[T]) -> T: return xs[0] print(first([1, 2, 3, 4, 5])) # 1
Take
APL
3↑1 2 3 4 5= → =1 2 3
Python
from typing import List, TypeVar T = TypeVar('T') def take(n: int, xs: List[T]) -> List[T]: return xs[:n] print(take(3, [1, 2, 3, 4, 5])) # [1, 2, 3]
Drop
APL
2↓1 2 3 4 5= → =3 4 5
Python
from typing import List, TypeVar T = TypeVar('T') def drop(n: int, xs: List[T]) -> List[T]: return xs[n:] print(drop(2, [1, 2, 3, 4, 5])) # [3, 4, 5]
Reverse First
APL
⊖ 3 3 ⍴ ⍳9= → =(7 8 9)(4 5 6)(1 2 3)
Python
from typing import List, Any def reverse_first(matrix: List[List[Any]]) -> List[List[Any]]: return matrix[::-1] print(reverse_first([[1, 2, 3], [4, 5, 6], [7, 8, 9]])) # [[7, 8, 9], [4, 5, 6], [1, 2, 3]]
Membership
APL
2 3 4 ∊ 1 2 3 5= → =1 1 0
Python
from typing import List, TypeVar T = TypeVar('T') def membership(xs: List[T], ys: List[T]) -> List[bool]: return [x in ys for x in xs] print(membership([2, 3, 4], [1, 2, 3, 5])) # [True, True, False]
Unique / Nub
APL
∪ 1 2 3 2 1 4 5 4= → =1 2 3 4 5
Python
from typing import List, TypeVar T = TypeVar('T') def unique(xs: List[T]) -> List[T]: return list(dict.fromkeys(xs)) print(unique([1, 2, 3, 2, 1, 4, 5, 4])) # [1, 2, 3, 4, 5]
Grade Up
APL
⍋ 3 1 4 1 5 9 2 6= → =2 4 1 7 3 5 8 6
Python
from typing import List, TypeVar T = TypeVar('T') def grade_up(xs: List[T]) -> List[int]: return sorted(range(len(xs)), key=lambda i: xs[i]) print(grade_up([3, 1, 4, 1, 5, 9, 2, 6])) # [1, 3, 6, 0, 2, 4, 7, 5]
Grade Down
APL
⍒ 3 1 4 1 5 9 2 6= → =6 8 5 3 1 7 4 2
Python
from typing import List, TypeVar T = TypeVar('T') def grade_down(xs: List[T]) -> List[int]: return sorted(range(len(xs)), key=lambda i: xs[i], reverse=True) print(grade_down([3, 1, 4, 1, 5, 9, 2, 6])) # [5, 7, 4, 2, 0, 6, 3, 1]
TODO Encode
APL
2 3 4 ⊤ 29= → =1 1 1
Python
from typing import List def encode(bases: List[int], n: int) -> List[int]: result = [] for base in reversed(bases): result.append(n % base) n //= base return list(reversed(result)) print(encode([2, 3, 4], 29)) # [1, 1, 1]
TODO Decode
APL
2 3 4 ⊥ 1 1 1= → =29
Python
import functools from typing import List def decode(bases: List[int], digits: List[int]) -> int: return sum(d * functools.reduce(lambda x, y: x*y, bases[:i], 1) for i, d in enumerate(digits)) print(decode([2, 3, 4], [1, 1, 1])) # 29
TODO Interval Index
APL
3 5 7 ⍸ 1 3 4 5 6 8= → =0 1 1 2 2 3
Python
import bisect from typing import List def interval_index(intervals: List[int], values: List[int]) -> List[int]: return [bisect.bisect_left(intervals, v) for v in values] print(interval_index([3, 5, 7], [1, 3, 4, 5, 6, 8])) # [0, 1, 1, 2, 2, 3]
TODO Matrix Inverse
APL
⌹ 2 2 ⍴ 1 2 3 4= → =(¯2 1)(1.5 ¯0.5)
Python
import numpy as np from typing import List def matrix_inverse(matrix: List[List[float]]) -> List[List[float]]: return np.linalg.inv(np.array(matrix)).tolist() print(matrix_inverse([[1, 2], [3, 4]])) # [[-2.0, 1.0], [1.5, -0.5]]
Shape
APL
⍴ 3 3 ⍴ ⍳9= → =3 3
Python
from typing import List, Any def shape(matrix: List[List[Any]]) -> List[int]: return [len(matrix), len(matrix[0])] print(shape([[1, 2, 3], [4, 5, 6], [7, 8, 9]])) # [3, 3]
Plus Reduce (Sum)
APL
+/ 1 2 3 4 5= → =15
Python
from typing import List def plus_reduce(xs: List[int]) -> int: return sum(xs) print(plus_reduce([1, 2, 3, 4, 5])) # 15
Multiply Reduce (Product)
APL
×/ 1 2 3 4 5= → =120
Python
import math from typing import List def multiply_reduce(xs: List[int]) -> int: return math.prod(xs) print(multiply_reduce([1, 2, 3, 4, 5])) # 120
Minimum Reduce
APL
⌊/ 1 2 3 4 5= → =1
Python
from typing import List def minimum_reduce(xs: List[int]) -> int: return min(xs) print(minimum_reduce([1, 2, 3, 4, 5])) # 1
Maximum Reduce
APL
⌈/ 1 2 3 4 5= → =5
Python
from typing import List def maximum_reduce(xs: List[int]) -> int: return max(xs) print(maximum_reduce([1, 2, 3, 4, 5])) # 5
Index of First Match
APL
2 3 4 ⍳ 3= → =2
Python
from typing import List def index_of_first_match(xs: List[int], x: int) -> int: try: return xs.index(x) except ValueError: return -1 # If not found, return -1 (APL would return length + 1) print(index_of_first_match([2, 3, 4], 3)) # 1
Thread
APL
thread ← {⍺⍺ ⊃∘.,⊂⍵}
⍝ Example 1:
identity thread 1 2 3
⍝ Result: 1 2 3
⍝ Example 2:
(identity add_one square) thread 1 2 3
⍝ Result: 1 2 1 2 3 4 1 4 9
⍝ Where:
⍝ identity ← {⍵}
⍝ add_one ← {⍵+1}
⍝ square ← {⍵*2}
Python
from typing import List, Callable, TypeVar T = TypeVar('T') def thread(xs: List[T], funcs: List[Callable[[T], T]]) -> List[T]: return [f(x) for x in xs for f in funcs] def identity(x: T) -> T: return x def add_one(x: int) -> int: return x + 1 def square(x: int) -> int: return x * x print(thread([1, 2, 3], [identity])) # [1, 2, 3] print(thread([1, 2, 3], [identity, add_one, square])) # [1, 2, 1, 2, 3, 4, 1, 4, 9]
Apply Transforms
APL
apply_transformations ← {
elements ← ⍺
transformations ← ⍵
{⊃⍵ {⍺⍺ ⍵}/ ⊂⍺}¨ elements ⊢ transformations
}
Python
from typing import List, Callable, TypeVar T = TypeVar('T') def apply_transformations(l: List[T], t: List[Callable[[T], T]]) -> List[T]: result = l.copy() for transform in t: result = [transform(item) for item in result] return result def remove_non_alphabetic_chars(s: str) -> str: return ''.join(c if c.isalpha() or c.isspace() else '' for c in s) def lowercase(s: str) -> str: return s.lower() def disemvowel(s: str) -> str: return ''.join(c for c in s if c.lower() not in 'aeiou') def dedupe(s: str) -> str: result = [] for char in s: if not result or char != result[-1]: result.append(char) return ''.join(result) def add_exclamation(s: str) -> str: return s + "!" transformations = [ lowercase, remove_non_alphabetic_chars, disemvowel, dedupe, add_exclamation ] utterances = [ "Secret speech Hazelton and obviously disemvowelled", "The quick brown fox jumps over the lazy dog!", "URL: https://www.example.com" ] print(apply_transformations(utterances, transformations))
2026 Review: APL in the Modern Array Programming Landscape
This section surveys developments in the APL ecosystem as of 2026, examining how the language family has evolved, what competitors have emerged, and where array programming now intersects with mainstream scientific computing.
APL Family Tree
The APL lineage spans six decades of array-oriented language design. The diagram below traces the major descendant languages and dialect branches.
Dyalog APL 19.0 (2025)
Dyalog APL 19.0 shipped in 2025 with several noteworthy additions:
- Link 4.0 — bidirectional sync between APL source and the filesystem, enabling standard version-control workflows without the traditional workspace model.
- Dyalog Python bridge improvements — tighter interop with NumPy arrays, reducing friction when mixing APL array operations with Python ML libraries.
- RIDE 4.4 — the web-based IDE gained responsive layout and better notebook- style cell execution, narrowing the gap with Jupyter.
- Safer function trains — the interpreter now rejects certain ambiguous fork/atop constructions at parse time rather than producing silent errors.
- APL on ARM64 — official builds for Apple Silicon and Linux ARM, reflecting the shift in workstation hardware.
The Dyalog contest (annual since 2011) continues to be the primary on-ramp for new practitioners; the 2025 edition drew entrants from 47 countries and introduced a collaborative team category.
BQN as the Modern APL Successor
BQN (Marshall, 2020) has emerged as the most actively developed array language for practitioners who want APL semantics without legacy baggage:
- Consistent namespace model — everything is a first-class value; no workspace-level globals by convention.
- Typed arrays from the start — the type system distinguishes numbers, characters, and nested arrays at the spec level.
- CBQN — the C reference implementation achieves performance competitive with J on many benchmarks; an LLVM backend (mlochbaum/cbqn) is under active development.
- VS Code extension — the BQN language server provides hover documentation and symbol completion for all glyphs, removing the glyph-entry barrier that slowed APL adoption.
- Community growth — the
#bqnchannel on the APL Farm Discord and the BQN subreddit have become the primary venues for newcomer questions, with response times typically under an hour.
BQN has not yet displaced Dyalog or J in production, but it has become the default recommendation for programmers learning array languages from scratch.
Array Programming Renaissance
The broader scientific-computing ecosystem has quietly converged on APL-style thinking, accelerating interest in the original languages:
NumPy and JAX
NumPy (1995/2006) and JAX (2018) are APL in spirit: rank-polymorphic operations,
broadcasting rules that mirror APL's rank operator, and tacit composition via
jax.vmap / jax.grad. Practitioners who master APL find NumPy idioms
immediately legible, and vice versa. The JAX team has cited Futhark and APL as
design influences for its functional transformations.
Uiua
Uiua (2023, rhymes with "sequoia") takes the array paradigm further toward the stack-based end. Key properties:
- Fully stack-based: no named function arguments.
- Glyphs map to single keystrokes via a custom input method.
- Built-in formatter enforces canonical whitespace and glyph choice.
- Ships an interactive online playground at uiua.org.
Uiua is not a direct APL descendant but shares the philosophy that array operations should compose without auxiliary variables. It has attracted attention from APL veterans who want a more regular syntax.
NumPy interop example
import numpy as np # APL outer product ∘.× in NumPy xs = np.array([1, 2, 3]) ys = np.array([10, 20, 30]) outer = np.outer(xs, ys) print(outer) # [[ 10 20 30] # [ 20 40 60] # [ 30 60 90]] # APL grade-up ⍋ in NumPy arr = np.array([3, 1, 4, 1, 5, 9, 2, 6]) grade_up_indices = np.argsort(arr, stable=True) print(grade_up_indices) # [1 3 6 0 2 4 7 5]
APL Competition and Problem-Solving Community
The competitive and educational community around APL has grown substantially since 2020:
Dyalog APL Problem Solving Competition
Dyalog's annual contest (https://problems.tryapl.org) offers two tracks:
- Student — prize money and Dyalog licenses; historically 200–400 entrants.
- Developer — open to all; no restriction on APL dialect used.
Phase I consists of ten warm-up puzzles solvable in one to three APL expressions. Phase II requires documented solutions with time-complexity analysis. The contest is a primary vehicle for discovering idiomatic APL patterns not covered in textbooks.
APL Quest
APL Quest (hosted on Dyalog's YouTube channel and mirrored on the APL Wiki) is a weekly live-coding series in which Adám Brudzewsky works through past competition problems, explaining multiple solution approaches. As of early 2026, over 200 episodes have been published, covering problems from the 2013–2025 competitions.
The episode transcripts are indexed at aplwiki.com/wiki/APLQuest and constitute one of the most comprehensive records of APL idiom evolution available online.
APL Farm Discord
The APL Farm Discord (invite at aplwiki.com) hosts channels for APL, J, K, BQN, and Uiua. Cross-language comparison threads are common, and maintainers of all major implementations are present. The server averaged 800+ active members per month in 2025.
References and Further Reading
- Iverson, K.E. (1962). A Programming Language. Wiley.
- Hui, R.K.W. & Iverson, K.E. (1990). J Introduction and Dictionary.
- Dyalog Ltd. (2025). Dyalog APL Version 19.0 Release Notes. https://docs.dyalog.com/19.0/
- Marshall, N. (2020). BQN: An Array Language. https://mlochbaum.github.io/BQN/
- Eirik Albrigtsen. Uiua Language Reference. https://uiua.org/docs
- APL Wiki. APL Quest episode index. https://aplwiki.com/wiki/APL_Quest
- Bradbury, J. et al. (2018). JAX: composable transformations of Python+NumPy programs. https://github.com/google/jax
