Home · Install · Process workers
A Session retains APL names. Calls return an Array; .py converts to Python values.
from miniapl import Session
apl = Session()
apl('v←⍳5')
assert apl('+/v').py == 15.0
Keywords bind values before evaluation. Bindings persist. Use indexing to read expressions or assign names.
assert apl('+/x', x=[1, 2, 3]).py == 6
apl['x'] = [[1, 2], [3, 4]]
assert apl['x'].shape == (2, 2)
Calls print only explicit output (⎕← and display commands). .eval() captures it instead.
r = apl.eval('⎕←x ⋄ x+1x', x=3)
assert r.value.py == 4
assert r.output == ['3x']
.run() also captures implicit expression display, for an APL-style notebook frontend. In ordinary Python notebooks, append ; to suppress display of a call’s return value.
Use with Session() as apl: or call apl.close() when finished. Returned arrays remain usable after closing.
Array retains shape, nesting, numeric domain and empty prototypes. .shape is a tuple; .apl is APL display. .py converts scalars to Python numbers and character vectors to strings. Other arrays become NumPy arrays.
from fractions import Fraction
from miniapl import Array
assert Array(2).apl == '2x'
assert Array(2.).apl == '2'
assert Array(Fraction(1, 3)).py == Fraction(1, 3)
| Python input | APL value |
|---|---|
int, bool, NumPy integers |
Exact number |
float |
Approximate real |
Fraction |
Exact rational |
complex |
Approximate complex |
str |
Character vector |
| Rectangular list/tuple | Ordinary array |
| Ragged list/tuple | Nested array |
| NumPy ndarray | Array preserving shape and numeric domain |
.np or np.asarray(a) always produces an ndarray. Simple numeric arrays use int64, float64 or complex128; fractions, large integers, nesting and mixed exact/approximate values use object. Character arrays use U1.
import numpy as np
a = Array([[1, 2, 3], [4, 5, 6]])
np.testing.assert_array_equal(a.np, [[1, 2, 3], [4, 5, 6]])
Python/NumPy conversions copy. Passing an Array back to miniapl shares its immutable value. Keep Array to preserve empty prototypes and exact nesting. NumPy is needed only for ndarray conversion.
Arithmetic uses APL agreement, aligning leading axes. Indexing starts at 1; : selects a whole axis.
np.testing.assert_array_equal(a + [10, 20], [[11, 12, 13], [24, 25, 26]])
np.testing.assert_array_equal(a[2, :], [4, 5, 6])
np.testing.assert_array_equal(a[:, [1, 3]], [[1, 3], [4, 6]])
Comparisons return arrays. % uses Python’s operand order for residue; // floors division; @ is matrix product. &/| give LCM/GCD, hence AND/OR on Booleans. len and iteration use major cells. Use member(x, y) for membership. Bounded slices are not supported.
.fn() creates a callable APL function. One argument supplies ⍵; two supply ⍺, ⍵. Arguments pass directly as values.
mean = apl.fn('{(+/⍵)÷≢⍵}')
assert mean([1, 2, 4]).py == Fraction(7, 3)
assert mean([1., 2., 4.]).py == 7 / 3
add = apl.fn('+')
np.testing.assert_array_equal(add([1, 2, 3], 10), [11, 12, 13])
.fn('g') follows later redefinitions of g; apl('g') retains its current function. Dfns and late-bound functions keep their originating session for name lookup. Close that session only after their last use.
Function arrays retain callable handles, including their session for name lookup. first and pick return the selected function:
from miniapl import first, pick
fs = apl('+‿×‿÷')
assert pick(2, fs)(2, 3).py == 6
assert first(fs)(2, 3).py == 5
Array([f, g]) also constructs a function vector; .py and object-dtype .np export Python callables. Functions from different sessions cannot be combined.
Primitives also have Python names. Dyadic names bind the right argument when called with one argument; .left(x) binds the left.
from miniapl import plus, times, subtract, tally
assert times(2)(3).py == 6
assert subtract(2)(5).py == 3 # 5−2
assert subtract.left(2)(5).py == -3 # 2−5
mean = plus.reduce() / tally
assert mean([1, 2, 4]).py == Fraction(7, 3)
Names distinguish valences: sign/times, shape/reshape, iota/index_of, mix/take. times(2.) binds an approximate number; times(2) an exact one. Operator methods use the underlying APL function.
| Python | APL |
|---|---|
f.reduce(), f.scan() |
f/, f\ |
f.each(), f.commute() |
f¨, f⍨ |
f.outer(), f.inner(g) |
f⌝, f.g |
f.key(), f.stencil(s) |
f⌸, f⌺s |
f.rank(r), f.atop(g) |
f⍤r, f⍤g |
f.beside(g), f.over(g), f.behind(g) |
f∘g, f⍥g, f⍛g |
f.power(n), f.at(i) |
f⍣n, f@i |
f.history(n) |
f⍣\n (count or predicate) |
f.with_inverse(g), f.under(g) |
f⇄g, f⌾g |
f.derivative() |
f∂ |
f[k] |
f[k] axis qualifier |
Arithmetic between functions makes forks: f+g is (f+g). f @ g is inner product; f << g is compose; f >> g reverses composition; f ** n is power. Python’s precedence applies when building these expressions.
Math families: prime/prime_mode (ℙ), factors/factor_spec (Ⓠ), polynomial/polyval (Ⓟ). windows is dyadic ↕.
from miniapl import prime, factors, polyval
assert prime(10).py == 29
np.testing.assert_array_equal(factors(700), [2, 2, 5, 5, 7])
p = polyval.left([1, 2, 3])
assert p(2).py == 17
assert p.derivative()(2).py == 14
np.testing.assert_array_equal(p.derivative()([10, 20], [1, 2]), [80, 280])
np.testing.assert_array_equal(plus.left(1).history(3)(0), [0, 1, 2, 3])
AplError provides the diagnostic, kind, message, source, byte span, call context and prior output. Completed assignments survive errors. .eval() retains output on the exception; ordinary calls print it before raising.
Ctrl-C interrupts evaluation. From another thread, call apl.interrupt(). Session(timeout=2) gives each evaluation a two-second deadline; adjust apl.timeout, or set it to None.
Sessions use a Rust worker thread and remain usable after cooperative interruption. Native-library calls can delay cancellation. For a hard-kill fallback, use a process worker.
Close the session after its last use:
apl.close()