find_dyalog
def find_dyalog():Locate the Dyalog interpreter binary
apl magicsapl magics for Jupyter and IPython
aplnb runs Dyalog APL from Python and provides apl magics for Jupyter and IPython. It uses the RIDE protocol, as do Dyalog’s IDE and official Jupyter kernel. It needs no extra code loaded into the APL workspace.
RIDE messages distinguish output, errors, and readiness for input. The examples below show the protocol before building the Apl session object.
find_dyalog first looks for mapl or dyalog on PATH. It then checks standard installation directories on macOS and Linux.
RIDE supports two connection directions. With RIDE_INIT=SERVE:*:port, the interpreter listens for a client. Dyalog’s Jupyter kernel uses this mode and polls for the listening port.
aplnb uses CONNECT mode. It listens on a port allocated by the OS, then starts Dyalog with that address. accept() waits for the interpreter to connect. This avoids selecting an unused port before binding it. The environment also sets RIDE_SPAWNED=1; explicit shutdown is covered below.
Spawn a Dyalog interpreter that connects back to us over RIDE; return (socket,Popen)
The interpreter speaks first. Here are the raw bytes:
A RIDE message contains a 4-byte big-endian total length, the literal RIDE, and a UTF-8 payload. Here 0x1c is 28: 4 length bytes, 4 bytes for RIDE, and 20 for SupportedProtocols=2.
The first two messages in each direction negotiate the protocol version as plain strings. Later payloads are JSON arrays containing a command name and its arguments.
Dyalog sometimes sends raw control characters inside JSON strings. ride_recv escapes these before decoding, as does the official Jupyter kernel.
Receive one RIDE message, JSON-decoded unless it’s a handshake string
Send one RIDE message: a handshake str, or a [cmd,args] list sent as JSON
Each side sends SupportedProtocols=2 and UsingProtocol=2. The client then sends Identify and reads the interpreter’s details:
'UsingProtocol=2'
{'Vendor': 'Dyalog Limited',
'Language': 'APL',
'version': '20.0.53963',
'arch': 'Unicode/64',
'platform': 'Mac-64'}
Wait for SetPromptType with type 1, which signals readiness for input:
Execute sends session input with a required trailing newline. Dyalog echoes the input and sends output in AppendSessionOutput messages. SetPromptType 1 signals that execution has finished and the interpreter is ready for more input.
[['UpdateSessionCaption', {'text': 'CLEAR WS - Dyalog APL'}],
['AppendSessionOutput', {'result': '3×⍳4\n', 'type': 14, 'group': 0}],
['SetPromptType', {'type': 0}],
['AppendSessionOutput', {'result': '3 6 9 12\n', 'type': 2, 'group': 0}],
['SetPromptType', {'type': 1}]]
Output type 14 is an input echo. HadError reports an error independently of its displayed text:
[['AppendSessionOutput', {'result': '1÷0\n', 'type': 14, 'group': 0}],
['SetPromptType', {'type': 0}],
['HadError', {'error': 11, 'dmx': 1}],
['AppendSessionOutput',
{'result': 'DOMAIN ERROR: Divide by zero\n', 'type': 5, 'group': 0}],
['AppendSessionOutput', {'result': ' 1÷0\n', 'type': 5, 'group': 0}],
['AppendSessionOutput', {'result': ' ∧\n', 'type': 5, 'group': 0}],
['SetPromptType', {'type': 1}]]
AplError includes Dyalog’s error display. Its reset flag indicates that the interpreter was replaced and workspace state was lost. AplPrompt is an internal exception for prompts other than the ready prompt.
The session stopped at a non-ready prompt: 2=⎕ input, 3=incomplete input, 4=⍞ input
An APL error, carrying the session’s error display as its message; reset means the interpreter was replaced and workspace state lost
ride_run sends all non-blank input lines in one Execute message. It collects output, excluding input echoes and prompt strings. The result is (output, errno), with error number 0 for success.
A final prompt counts only after Dyalog has echoed every input line. This matters for multiline blocks, as the next example shows.
Run one or more lines of APL in the session; return (output,errno) once the session is ready again
Dyalog 20.0 enables multiline session input by default. Send a complete block in one Execute message. The interpreter echoes each line and uses prompt type 3 while the block is open.
A type 3 prompt before all line echoes means Dyalog is still reading the block. After all echoes, it means the submitted block is incomplete. Do not send another Execute at that prompt: the interpreter can crash. Apl.run handles incomplete input by replacing the interpreter.
Session state persists across calls:
Errors come back with the HadError number and the session error display:
Apl starts Dyalog and completes the handshake before accepting input. It sets the print width to 32767, matching the official kernel, to avoid wrapping long output.
A Dyalog APL session over the RIDE protocol
close sends Exit and waits up to three seconds. It kills the process if it has not exited. _connect registers this cleanup with atexit to avoid leaving Dyalog processes behind. Closing the socket alone does not reliably shut down a stuck interpreter.
run returns output or raises AplError:
⎕ or ⍞ input are cancelled. The session remains usable.:If, requires a new interpreter because of Dyalog/ride#1401. The exception has reset=True, indicating that workspace state was lost.Run code in the session, returning its output; raises AplError on APL errors
The matrix remains available for later calls. Ordinary APL errors do not reset the session:
Calling apl(code) displays output without an extra print. It returns AplOut, a str subclass that preserves Dyalog’s formatting in notebook displays. Results still support ordinary string operations. Calls with no output return None.
Run code, returning session output (or None if there is none)
Output text from an Apl call; displays verbatim, in the SAX2 APL font where HTML is available
pyval returns a Python value instead of display text. It asks Dyalog to serialize the expression with ⎕JSON, then parses the JSON.
The HighRank option serializes arrays of rank 2 or higher as nested lists. Without it, ⎕JSON rejects these arrays. The left argument 1 forces serialization. Monadic ⎕JSON would try to parse a character vector as JSON.
Use square brackets to read an APL expression as a Python value or assign a Python value to an APL variable:
⎕JSON imports nested lists as vectors of vectors. Use ↑ to make a rank-2 matrix:
fn returns a Python callable for an APL function. One argument calls it monadically. Two arguments call it dyadically, with the left argument first. Arguments and results use the same JSON conversions as pyval and assignment.
A Python callable applying APL function code monadically or dyadically
Use a context manager to close a session before process exit:
Check that cancelled input requests leave the session usable:
Check that incomplete input resets the workspace:
The timeout parameter limits startup. Once connected, Apl.run waits as long as the computation takes:
apl magics%%apl runs a cell and displays its session output. A trailing ; suppresses the display. %apl expr returns a Python value, as with apl[expr], and can appear in an assignment: z = %apl z.
The first magic call starts the interpreter and adds the APL language bar to the page. Registration alone does not start Dyalog.
Output uses Adám Brudzewsky’s SAX2 APL font, loaded locally or from a CDN. Monospace is the fallback when SAX2 is unavailable.
IPython %apl/%%apl magics, driving a lazily-started Apl session
Create an APLMagic and register its apl line/cell magic with shell, returning it
The line magic brings values back into Python:
The first example runs ]display, a Dyalog user command. The second suppresses cell output with a trailing ;.
┌→──────────┐ ↓ ┌→─┐ ┌→─┐ │ │ │ab│ │cd│ │ │ └──┘ └──┘ │ │ │ │ 1 2 │ │ │ └∊──────────┘
Required function for creating magic
Shut down the sessions this notebook started: the magic’s, the Apl object’s, and the raw-socket walkthrough one.