# Dyalog sessions


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

Import `Apl` and `AplError` from `basedpl.dyalog`. Sessions return
Dyalog output or JSON-converted Python values, not bAsedPL 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.

``` python
from fastcore.test import *
```

## Connecting

------------------------------------------------------------------------

### start_dyalog

``` python
def start_dyalog(
    dyalog:NoneType=None, # Path to the interpreter binary; `find_dyalog()` result if None
    timeout:int=10, # Socket timeout during the startup handshake, in seconds
):
```

*Spawn a Dyalog interpreter that connects back to us over RIDE; return
`(socket,Popen)`*

------------------------------------------------------------------------

### find_dyalog

``` python
def find_dyalog():
```

*Locate the Dyalog interpreter binary*

`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_recv

``` python
def ride_recv(
    sock
):
```

*Receive one RIDE message, JSON-decoded unless it’s a handshake string*

------------------------------------------------------------------------

### ride_send

``` python
def ride_send(
    sock, msg
):
```

*Send one RIDE message: a handshake `str`, or a `[cmd,args]` list sent
as JSON*

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`.

------------------------------------------------------------------------

### ride_run

``` python
def ride_run(
    sock, code
):
```

*Run APL lines; return `(output,errno)` once the session is ready again*

------------------------------------------------------------------------

### AplPrompt

``` python
def AplPrompt(
    ptype
):
```

*A non-ready prompt: 2=⎕ input, 3=incomplete input, 4=⍞ input*

------------------------------------------------------------------------

### AplError

``` python
def AplError(
    msg, reset:bool=False
):
```

*A Dyalog diagnostic; `reset` means the interpreter was replaced and
workspace state lost*

------------------------------------------------------------------------

### Apl.\_\_exit\_\_

``` python
def __exit__(
    *args
):
```

------------------------------------------------------------------------

### Apl.\_\_enter\_\_

``` python
def __enter__():
```

------------------------------------------------------------------------

### Apl.close

``` python
def close():
```

*Shut down the interpreter and close the connection*

------------------------------------------------------------------------

### Apl

``` python
def Apl(
    dyalog:NoneType=None, timeout:int=10
):
```

*A Dyalog APL session over the RIDE protocol*

## Evaluating APL

------------------------------------------------------------------------

### Apl.run

``` python
def run(
    code
):
```

*Run `code`, returning session output; raises `AplError` on APL errors*

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

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

    '6\n'

------------------------------------------------------------------------

### Apl.\_\_call\_\_

``` python
def __call__(
    code
):
```

*Run `code`, returning displayable session output, or None if there is
none*

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

``` python
test_is(dyalog('quiet←7'), None)
dyalog('2×x')
```

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

## Python values

------------------------------------------------------------------------

### Apl.pyval

``` python
def pyval(
    expr
):
```

*Evaluate `expr` and return its JSON-converted Python value*

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

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

------------------------------------------------------------------------

### Apl.\_\_setitem\_\_

``` python
def __setitem__(
    nm, v
):
```

------------------------------------------------------------------------

### Apl.\_\_getitem\_\_

``` python
def __getitem__(
    expr
):
```

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

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

------------------------------------------------------------------------

### Apl.fn

``` python
def fn(
    code
):
```

*A Python callable applying APL function `code` monadically or
dyadically*

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

``` python
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`, which is separate from
bAsedPL’s exception. Completed assignments remain in the session:

``` python
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. An incomplete-input prompt
restarts the interpreter and raises `AplError` with `reset=True`; that
restart loses the workspace.

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

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

    55
