Metadata-Version: 2.4
Name: aplnb
Version: 0.3.0
Summary: MiniAPL in notebooks, with apl magics for Jupyter and IPython
Author-email: Jeremy Howard <info@answer.ai>
License: Apache-2.0
Project-URL: Repository, https://github.com/answerdotai/aplnb
Project-URL: Documentation, https://answerdotai.github.io/aplnb
Keywords: nbdev,jupyter,notebook,python,apl,basedpl
Classifier: Natural Language :: English
Classifier: Intended Audience :: Developers
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastcore
Requires-Dist: basedpl>=0.1.4
Requires-Dist: ipython
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: fastcdp>=0.0.18; extra == "dev"
Requires-Dist: numpy; extra == "dev"
Dynamic: license-file

# aplnb


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

`aplnb` brings [MiniAPL](https://github.com/AnswerDotAI/basedpl) 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`](https://answerdotai.github.io/aplnb/core.html) for the implementation. For the J language, see the sibling project [jnb](https://github.com/AnswerDotAI/jnb).

## Installation

Install aplnb and its MiniAPL runtime:

``` sh
pip install aplnb
```

No separate APL installation is needed. To load the magics automatically in IPython and Jupyter, run:

``` sh
aplnb_install
```

Or run `%load_ext aplnb` in an individual notebook. The interpreter starts on the first use of a magic.

## Native arrays

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:

``` python
v = %apl 1 2 4
v
```

    [1., 2., 4.]

Python operators use MiniAPL’s array semantics. Python determines precedence, so this multiplies before adding:

``` python
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:

``` python
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:

``` python
from basedpl import Array, plus, tally
```

``` python
nested = %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:

``` python
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:

``` python
v.apl, exact.apl
```

    ('1 2 4', '1x 2x 4x')

## The `apl` magics

Hold **left Alt/Option** for [MiniAPL’s glyph keyboard](https://answerdotai.github.io/basedpl/keyboard.html): 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](https://abrudz.github.io/lb/apl). 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](https://github.com/abrudz/SAX2) APL font:

``` python
%%apl
m←3 3⍴⍳9
m×10
```

<pre class="aplnb_out sax2">10 20 30
40 50 60
70 80 90</pre>

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:

``` python
v = %apl 3×⍳4
v.np
```

    array([ 3.,  6.,  9., 12.])

``` python
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:

``` python
z = %apl m
z.np
```

    array([[1., 2., 3.],
           [4., 5., 6.],
           [7., 8., 9.]])

To suppress a cell’s output, end the last line with a `;`:

``` python
%%apl
m×10;
```

`⎕←` displays a value explicitly, which is how you show something that would otherwise be shy:

``` python
%%apl
v←2×⍳5
⎕←v
```

<pre class="aplnb_out sax2">2 4 6 8 10</pre>

Convert to NumPy to use its methods:

``` python
a = %apl m
a.np.sum(axis=0)
```

    array([12., 15., 18.])

### Example algorithms

The fibonacci sequence:

``` python
fib = %apl {⍵,+/¯2↑⍵}⍣15⊢1 1
fib.np
```

    array([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 1`: Initial seed (first two Fibonacci numbers)
2.  `{⍵,+/¯2↑⍵}`: Function that appends the sum of the last two elements
3.  `⍣15`: Apply the function 15 times
4.  `⊢`: Identity function, passes the initial argument (1 1) to the iteration

Prime number sieve:

``` python
%%apl
primes ← {⍵×2=+⌿0=⍵∘.|⍵}⍳
(primes 50)~0
```

<pre class="aplnb_out sax2">2 3 5 7 11 13 17 19 23 29 31 37 41 43 47</pre>

Explanation:

1.  `⍳50` generates integers 1 to 50
2.  `⍵∘.|⍵` creates a matrix of remainders, with candidate divisors in the rows
3.  `0=` marks the entries with no remainder
4.  `+⌿` sums columns, counting divisors for each number
5.  `2=` selects numbers with exactly two divisors
6.  `⍵×` keeps those numbers and replaces the rest with zero
7.  `~0` removes the zeros

## Using MiniAPL from Python

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:

``` python
import numpy as np
from basedpl import Session
```

``` python
apl = Session()
apl('3 3⍴⍳9').np
```

    array([[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:

``` python
apl(x=np.arange(1, 6))
apl['v'] = [3,1,4,1,5]
apl['{⍵[⍋⍵]}v'].np
```

    array([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:

``` python
mean = apl.fn('{(+/⍵)÷≢⍵}')
mean([1,2,4]).py
```

    Fraction(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:

``` python
apl.close()
```

## Dyalog reference sessions

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:

``` python
from aplnb.dyalog import Apl
```

``` python
with Apl() as dyalog:
    total = dyalog.pyval('+/⍳10')
total
```

    55

`pyval` returns JSON-converted Python values. `run` returns session output as text. See [Dyalog sessions](https://answerdotai.github.io/aplnb/dyalog.html) for assignment, function calls and error handling.

## Errors and interruption

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](https://github.com/AnswerDotAI/basedpl) for supported language features and differences.

## Learning APL

To start learning APL, follow the [17 video series](https://forums.fast.ai/t/apl-array-programming/97188) run by Jeremy Howard, and have a look at the [study notes](https://fastai.github.io/apl-study/apl.html). These use Dyalog APL. Interpreter-specific features and user commands differ in MiniAPL.
