Dyalog sessions

Run Dyalog APL through RIDE for reference checks.

Import Apl and AplError from basedpl.dyalog. Sessions return Dyalog output or JSON-converted Python values, not BPL arrays. Importing this module does not start Dyalog or register notebook magics.

Install Dyalog separately. The examples use a local interpreter and run with nbdev-test --flags dyalog. The default test run skips them.

from fastcore.test import *

Connecting

find_dyalog checks the command path and standard macOS/Linux installation locations. Pass dyalog= to Apl to choose another executable. timeout bounds the startup handshake, not evaluation.

RIDE uses length-prefixed messages over a local socket. ride_run collects session output until Dyalog is ready for another expression. Interactive and incomplete-input prompts are handled by Apl.run.

Evaluating APL

run returns session output as text. Assignments persist across calls:

dyalog = Apl()
out = dyalog.run('x←1 2 3\n+/x')
test_eq(out.strip(), '6')
out
'6\n'

Calling a session returns AplOut, the same text with notebook display support. A silent assignment returns None:

test_is(dyalog('quiet←7'), None)
dyalog('2×x')
2 4 6

Python values

pyval uses Dyalog’s JSON conversion. Scalars become Python values and numeric arrays become lists. This is the interface used by the reference checker:

test_eq(dyalog.pyval('+/x'), 6)
matrix = dyalog.pyval('2 3⍴⍳6')
test_eq(matrix, [[1,2,3], [4,5,6]])
matrix
[[1, 2, 3], [4, 5, 6]]

Square brackets read an expression or assign a JSON-compatible Python value. Quotes in strings are escaped for APL:

dyalog['values'] = [3,1,4]
dyalog['message'] = "can't"
test_eq(dyalog['message'], "can't")
test_eq(dyalog['values'], [3,1,4])
dyalog['values']
[3, 1, 4]

fn converts Python arguments through JSON and evaluates the function expression on each call. One argument supplies ⍵; two supply ⍺ and ⍵:

mean = dyalog.fn('{(+/⍵)÷≢⍵}')
test_eq(mean([1,2,3]), 2)
add = dyalog.fn('+')
test_eq(add([1,2,3], 10), [11,12,13])
add([1,2,3], 10)
[11, 12, 13]

Errors and lifetime

Dyalog errors raise this module’s AplError, not BplError. Completed assignments remain in the session:

with expect_fail(AplError, contains='DOMAIN ERROR'): dyalog.run('saved←42 ⋄ 1÷0')
test_eq(dyalog['saved'], 42)
dyalog['saved']
42

Interactive ⎕ and ⍞ input is rejected. When Dyalog shows an incomplete-input prompt, Apl.run restarts the interpreter and raises AplError with reset=True. The restart loses the workspace.

Call close() when finished, or use a context manager for a bounded reference check:

dyalog.close()
with Apl() as reference: total = reference.pyval('+/⍳10')
test_eq(total, 55)
total
55