I/O Operations in APL
Read and write data in APL using quad output (⎕←), quote-quad (⍞), the format primitive (⍕), and the ⎕FIO file functions in GNU APL with Docker-ready examples
Input and output are where an otherwise pure, array-oriented language has to talk to the outside world. In the earlier tutorials a bare expression printed its result automatically — that is the only output APL needs for interactive exploration. But once you want to control what appears, build report lines, ask the user for data, or persist results to disk, you reach for APL’s dedicated I/O tools.
APL keeps these tools minimal and consistent with its array philosophy. Output is done by assigning to one of two system variables: ⎕ (quad) for normal display and ⍞ (quote-quad) for raw character output without a trailing newline. The same two symbols, read instead of assigned, perform input. Formatting numbers into clean text is the job of the ⍕ (format) primitive — a single glyph that replaces an entire printf format-string vocabulary. File access in GNU APL is provided by the ⎕FIO family of functions.
This tutorial covers console output, formatted output, keyboard input, and reading and writing files — adapting each to APL’s “operate on whole arrays” mindset rather than the character-at-a-time loops of imperative languages. We use GNU APL throughout, the APL2-style interpreter from the Docker image used across this series.
Console Output
You have already seen that a bare expression displays itself. For deliberate output, assign the value to ⎕ — the result is sent to the session and followed by a newline. Assign to ⍞ instead when you want the characters written with no trailing newline, so the next output continues on the same line.
Create a file named output.apl:
| |
The key distinction is the newline. ⎕← terminates its line, so each ⎕← lands on its own row. ⍞← leaves the cursor mid-line, which is how you stitch several pieces of text — or a prompt and its answer — into a single line. Notice also how , (catenate) joins a literal string with ⍕ 6 × 7: the format primitive turns the number 42 into the two characters 42 so they can sit alongside ordinary text.
Formatted Output
Numbers rarely come out of a computation in exactly the shape you want to show. The ⍕ (format) primitive is APL’s universal formatter. Used monadically (⍕ value) it produces the default character representation. Used dyadically it takes precision controls on the left: a single number sets the count of decimal places, and a pair sets width decimals for right-aligned, fixed-width columns.
Create a file named format.apl:
| |
2 ⍕ 3.14159 rounds to two decimals (3.14); 0 ⍕ 3.14159 rounds to a whole number (3). The two-element left argument 10 2 reserves a field ten characters wide and right-justifies the value within it — exactly what you want for aligning a column of figures. The last line shows the everyday pattern: compute a value, format it to a fixed precision, and catenate it onto a descriptive label. Because ⍕ works on arrays, the very same 2 ⍕ would format an entire vector of prices at once.
Reading Input
Input mirrors output: the same ⍞ and ⎕ symbols read instead of write when they appear on the right of an expression.
⍞(quote-quad) reads one line from the keyboard and returns it as a character vector — exactly what was typed, no interpretation.⎕(quad) reads one line and evaluates it as an APL expression, so typing3 1 4 1 5yields an actual five-element numeric vector.
Because both block while waiting for the keyboard, they belong in interactive sessions rather than batch scripts. Start an interactive APL session:
| |
Then try the following — the six-space indent marks what you type, and the lines below are APL’s response:
| |
With ⍞, name becomes the character vector Ada, ready to catenate into a greeting. With ⎕, the line 3 1 4 1 5 is evaluated into a numeric vector, so +/ nums sums it to 14. This is the division of labour to remember: ⍞ for raw text, ⎕ when you want APL to parse the input as data or even as an expression.
Reading and Writing Files
GNU APL accesses files through the ⎕FIO (file I/O) function family. These functions work with byte vectors — vectors of integers in the range 0–255 — so you convert between text and bytes with ⎕UCS, which maps characters to their Unicode code points and back. Reading a whole file is a single call — ⎕FIO.read_file returns a file’s bytes — while writing uses a short handle-based sequence: open the file with ⎕FIO.fopen, push the bytes with ⎕FIO.fwrite, and release it with ⎕FIO.fclose.
The example below writes three lines to a file, reads them back, and then processes the contents with ordinary array operations — no character-by-character loop in sight.
Create a file named file_io.apl:
| |
The flow is deliberately array-shaped. ⎕UCS 10 is the linefeed character, used both to join the lines for writing and, on the way back, to count them: raw = nl produces a boolean vector marking every newline, +/ adds those up, and 1 + accounts for the final line that has no trailing newline. Tallying lines becomes one expression rather than a loop with a counter. Likewise ≢ raw reports the total character count directly. Each of the handle ←, wrote ←, and closed ← assignments captures (and silently discards) its call’s result so it does not print — only the lines we explicitly send to ⎕ appear.
The same handle that ⎕FIO.fopen returns also drives incremental reads: ⎕FIO.fgets pulls one line at a time, whereas the ⎕FIO.read_file used above slurps an entire file at once — pick whichever fits your data. For line-oriented text there is also ⎕FIO.write_lines, which takes a nested vector of strings and appends a newline to each, a one-call alternative to assembling the byte vector by hand.
Running with Docker
| |
The file_io.apl run creates notes.txt inside the mounted $(pwd) directory, so after it finishes you will find the written file alongside your scripts on the host.
Expected Output
Running output.apl:
Implicit output: a bare expression is shown
Explicit output via quad-assignment
1 2 3 4 5
Six times seven is 42
Same line: joined by ⍞← then ⎕←
Running format.apl:
3.14159
3.14
3
3.14
Line total: $59.97
Running file_io.apl:
Wrote 32 characters to notes.txt
File contents:
APL file I/O
Line two
Line three
Total characters: 32
Number of lines: 3
Key Concepts
⎕←is normal output,⍞←is output without a newline — assign to⎕to display a value and end the line; assign to⍞to keep writing on the same line, ideal for prompts and joined text.⎕and⍞also read input — on the right of an expression,⍞returns a raw character vector while⎕evaluates the typed line into data; both block on the keyboard, so they suit interactive use.⍕(format) replaces printf — monadic for the default text form, dyadic withdecimalsorwidth decimalsfor fixed-precision, right-aligned columns, and it formats whole arrays at once.- Build report lines by catenating —
,joins a literal label with a formatted number ('Total: ', ⍕ value), since both sides are just character vectors. ⎕FIOis GNU APL’s file interface —⎕FIO.read_filereturns a whole file’s bytes in one call, while writing uses the⎕FIO.fopen→⎕FIO.fwrite→⎕FIO.fclosehandle sequence (or⎕FIO.write_linesfor line-oriented text).⎕UCSbridges text and bytes — file functions deal in byte vectors, so convert characters to code points (and back) with⎕UCS;⎕UCS 10is the newline character.- Process file contents as arrays, not loops — once a file is read into a vector, counting lines, measuring length, or filtering is just
+/,≢, and the primitives you already know. - Right-to-left evaluation still applies — I/O expressions like
⎕← 'Six times seven is ', ⍕ 6 × 7evaluate the rightmost term first, so the multiplication and formatting happen before the catenation and display.
Running Today
All examples can be run using Docker:
docker pull juergensauermann/gnu-apl-1.8:latest
Comments
Loading comments...
Leave a Comment