aplnb brings MiniAPL to Jupyter and IPython. Use %%apl for APL session output and %apl for native MiniAPL values in Python. Both share a persistent workspace.
See core for the implementation. For the J language, see the sibling project jnb.
Install aplnb and its MiniAPL runtime:
pip install aplnbNo separate APL installation is needed. To load the magics automatically in IPython and Jupyter, run:
aplnb_installOr run %load_ext aplnb in an individual notebook. The interpreter starts on the first use of a magic.
The line magic %apl returns a native basedpl.Array. Its immutable data stays in MiniAPL, retaining nesting, exact numbers and empty-array prototypes. No NumPy conversion is needed:
v = %apl 1 2 4
v[1., 2., 4.]
Python operators use MiniAPL’s array semantics. Python determines precedence, so this multiplies before adding:
v * 2 + 1[3., 5., 9.]
Indexing is one-based, as in APL. A full : selects an axis. Iteration yields rows of a matrix rather than individual elements:
a = %apl 2 3⍴⍳6
a.shape, a[1, :], a[:, 2], list(a)((2, 3), [1., 2., 3.], [2., 5.], [[1., 2., 3.], [4., 5., 6.]])
MiniAPL also exposes functions by their word names. plus.reduce() is +/; adding .each() applies it to each nested element:
from basedpl import Array, plus, tallynested = %apl (1 2)(3 4 5)
nested, plus.reduce().each()(nested)([Array([1., 2.]), Array([3., 4., 5.])], [3., 12.])
Python integers become exact values. Functions compose too: plus.reduce() / tally builds the mean function +/÷≢. Its result is still a native array, even when scalar:
exact = Array([1, 2, 4])
mean_native = plus.reduce() / tally
mean_native(exact)Fraction(7, 3)
The native representation uses Python numeric notation: 2 is exact and 2. is approximate. .apl returns the APL display as a string:
v.apl, exact.apl('1 2 4', '1x 2x 4x')
Hold left Alt/Option for MiniAPL’s glyph keyboard: Alt-h ←, Alt-minus ×, Alt-equals ÷, Alt-Shift-a ⍶. Right Option keeps its native behavior. Chords work in APL input, including strings and comments.
Type a backtick followed by a MiniAPL symbol name: `io then Tab inserts ⍳, and 2`times3 becomes 2×3. Suggestions appear beside the cursor as you type. Click a suggestion or keep typing to resolve an ambiguous name. Names and aliases come from MiniAPL’s REPL catalogue.
Completion is active in %%apl cells and on %apl lines, including x = %apl .... Ordinary Python and Markdown input is unchanged. Strings, comments and pasted text are not expanded. Tab explicitly completes an existing name. Enter accepts a unique match before the notebook’s normal newline or execution action. Escape or cursor movement cancels automatic expansion.
The first apl magic also adds a clickable symbol bar, based on Adám Brudzewsky’s APL language bar. Hover over a glyph to see its names. The ▲/▼ button switches between pushing the page down and overlaying it. This choice is remembered per site.
The cell magic (%%apl) displays MiniAPL session output in Adám’s SAX2 APL font:
%%apl
m←3 3⍴⍳9
m×1010 20 30 40 50 60 70 80 90
Assignments are shy: the m← line printed nothing. The line magic returns a native array. Use .np for a NumPy array; install NumPy with pip install numpy to run the conversion examples:
v = %apl 3×⍳4
v.nparray([ 3., 6., 9., 12.])
text = %apl 'APL in Python'
text.py'APL in Python'
.py converts numeric scalars to Python numbers and character vectors to strings. Other arrays become NumPy arrays. .np always returns a NumPy array. Both conversions copy the data:
z = %apl m
z.nparray([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
To suppress a cell’s output, end the last line with a ;:
%%apl
m×10;⎕← displays a value explicitly, which is how you show something that would otherwise be shy:
%%apl
v←2×⍳5
⎕←v2 4 6 8 10
Convert to NumPy to use its methods:
a = %apl m
a.np.sum(axis=0)array([12., 15., 18.])
The fibonacci sequence:
fib = %apl {⍵,+/¯2↑⍵}⍣15⊢1 1
fib.nparray([1.000e+00, 1.000e+00, 2.000e+00, 3.000e+00, 5.000e+00, 8.000e+00,
1.300e+01, 2.100e+01, 3.400e+01, 5.500e+01, 8.900e+01, 1.440e+02,
2.330e+02, 3.770e+02, 6.100e+02, 9.870e+02, 1.597e+03])
Explanation:
1 1: Initial seed (first two Fibonacci numbers){⍵,+/¯2↑⍵}: Function that appends the sum of the last two elements⍣15: Apply the function 15 times⊢: Identity function, passes the initial argument (1 1) to the iteration
Prime number sieve:
%%apl
primes ← {⍵×2=+⌿0=⍵∘.|⍵}⍳
(primes 50)~02 3 5 7 11 13 17 19 23 29 31 37 41 43 47
Explanation:
⍳50generates integers 1 to 50⍵∘.|⍵creates a matrix of remainders, with candidate divisors in the rows0=marks the entries with no remainder+⌿sums columns, counting divisors for each number2=selects numbers with exactly two divisors⍵×keeps those numbers and replaces the rest with zero~0removes the zeros
The magics use basedpl.Session. Calling a session returns a native array or function and prints explicit APL output. apl.eval(...) returns a Result with the native .value and captured .output without printing. Both suppress implicit APL display. Use .np or .py when you need a converted result:
import numpy as np
from basedpl import Sessionapl = Session()
apl('3 3⍴⍳9').nparray([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
Keyword arguments bind Python values in the workspace. Square brackets read an APL expression or assign a value:
apl(x=np.arange(1, 6))
apl['v'] = [3,1,4,1,5]
apl['{⍵[⍋⍵]}v'].nparray([1, 1, 3, 4, 5])
fn makes a composable Function from an APL function expression. Pass one argument for a monadic call or two for a dyadic call. Python integers stay exact, so .py converts this mean to a Fraction rather than a float:
mean = apl.fn('{(+/⍵)÷≢⍵}')
mean([1,2,4]).pyFraction(7, 3)
fn is late-bound: apl.fn('foo') follows later redefinitions of foo. Use create_magic(session=apl) to share a Python session with the magics.
Use a session as a context manager (with Session() as apl:), or close it when finished:
apl.close()Use aplnb.dyalog when you need Dyalog as an independent reference interpreter. Dyalog must be installed separately. This session API does not change the %apl or %%apl magics, which continue to use MiniAPL:
from aplnb.dyalog import Aplwith Apl() as dyalog:
total = dyalog.pyval('+/⍳10')
total55
pyval returns JSON-converted Python values. run returns session output as text. See Dyalog sessions for assignment, function calls and error handling.
APL errors raise basedpl.AplError. The magics display output produced before the error. Incomplete input raises a syntax error without resetting the workspace.
Interrupt a calculation with the notebook’s stop button. Set a per-evaluation deadline with Session(timeout=seconds) or magic.session.timeout = seconds. Sessions use a Rust worker thread. Cooperative cancellation preserves the workspace and completed assignments. Native-library calls and individual BigInt operations can delay cancellation; the thread is never forcibly killed.
MiniAPL implements a subset of Dyalog APL. See its README for supported language features and differences.
To start learning APL, follow the 17 video series run by Jeremy Howard, and have a look at the study notes. These use Dyalog APL. Interpreter-specific features and user commands differ in MiniAPL.