Python Equivalents for APL Functions

Table of Contents

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.

diagram-apl-family.png

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 #bqn channel 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

Author: Jason Walsh

j@wal.sh

Last Updated: 2026-04-19 08:30:00

build: 2026-04-20 23:45 | sha: d110973