I/O Operations in Dylan
Learn input and output in Dylan - formatted console output with format-out, reading from standard input, and reading and writing files with streams and with-open-file
Input and output in Dylan live in the io library, and they reflect the language’s Lisp heritage: everything is built around stream objects. A stream is just an object you read elements from or write elements to, and the same functions — read-line, write-line, read-to-end — work whether the stream is the console, a file, or an in-memory buffer. Console output has its own convenience layer, the format-out function, which you have already met in the Hello World page.
Because Dylan organizes code into libraries and modules, I/O also brings a practical lesson about the module system. The format-out function comes from the format-out module, but reading input and working with files needs the streams and standard-io modules. A source file only sees the names its module explicitly imports, so a program that does more than print needs a slightly richer library definition than the one-line Hello World used.
In this tutorial you’ll go beyond a single format-out call: you’ll format numbers in several radixes, read lines from standard input, and both write and read files using the with-open-file macro that opens a stream and reliably closes it again. Along the way you’ll see how Dylan’s uniform stream model makes file I/O look almost identical to console I/O.
Formatted Console Output
The format-out function takes a control string containing directives — each starts with % — followed by the values that fill them in. It is Dylan’s printf, and it is available by default in the simple single-file project the Docker image builds, so this example runs directly.
Create a file named hello.dylan:
| |
%suses an object’s message form; for a string that is just the text itself.%d,%b,%o,%xprint an integer in decimal, binary, octal, and hexadecimal — note the value is passed once per directive.%cprints a<character>such as the literal'D'.%=prints the same representation the debugger shows, so#(2, 3, 5, 7, 11)appears literally — perfect for lists and other collections.%%is how you get a literal%into the output.
Setting Up a Project for Input and Files
Reading input and touching files needs names from the streams and standard-io modules, so those examples require a small project rather than a lone source file. A Dylan project is three files: a library/module definition, a .lid file listing the sources, and the source itself.
Create a file named library.dylan:
| |
Create a file named io-demo.lid:
| |
The use io; line pulls in the I/O library; the module’s use streams; and use standard-io; then expose read-line, write-line, with-open-file, and the standard stream variables. The source examples below all begin with Module: io-demo so they compile inside this project. Build them with the compiler bundled in the image (see Running with Docker).
Reading from Standard Input
*standard-input* and *standard-output* are the console streams from the standard-io module. read-line pulls one line of text — without the trailing newline — and input always arrives as a string, so convert it with string-to-integer when you need a number. Flushing the prompt with force-output makes sure it appears before the program blocks waiting for the user.
Create a file named io-demo.dylan:
| |
*standard-input*/*standard-output*are ordinary stream objects, so the sameread-line/writefunctions work on them as on files.read-linereturns the line as a<string>with the newline removed.force-outputflushes buffered output — important for prompts that must appear before input is read.string-to-integer(fromcommon-dylan) parses text into an<integer>; there are matching converters likestring-to-float.
Given the input Ada and 36 (the lines you type are shown after each prompt), this program prints:
What is your name? Ada
Hello, Ada! Nice to meet you.
How old are you? 36
Next year you will be 37.
Reading and Writing Files
File access uses the same streams, obtained from the with-open-file macro. It opens a stream bound to a name, runs the body, and guarantees the stream is closed afterwards — even if the body signals an error. The direction: keyword chooses #"output" (write) or #"input" (read).
This example reuses the same project. Create a file named io-demo.dylan with these contents:
| |
with-open-file (stream = "poem.txt", direction: #"output")creates a writable file stream and closes it automatically at the end of the body.write-linewrites a string followed by a newline; there is alsowritefor output without the newline andnew-linefor just a line break.read-line(stream, on-end-of-stream: #f)returns#fat end of file instead of signalling an error, which lets thewhileloop stop cleanly.:=reassigns thelinebinding on each iteration —letbindings in Dylan are mutable.read-to-endslurps the rest of the stream into a string;contents.size(method-call syntax forsize(contents)) reports its length.
This program prints:
Wrote poem.txt
1: Roses are red,
2: Dylan dispatches on all,
3: Types picked at run time.
File is 66 characters long.
Running with Docker
The single-file console example runs with the documented one-liner — the image finds hello.dylan, wraps it in a project, compiles it, and runs the result:
| |
The input and file examples are full projects (library.dylan, io-demo.lid, io-demo.dylan in the current directory). Build them with the dylan-compiler that ships in the image by pointing a registry at the .lid file — the same mechanism the image uses internally. The -it flags keep standard input open for the interactive example:
| |
Expected Output
Running the console example (hello.dylan):
=== Formatted Output in Dylan ===
Language: Dylan
Dylan first appeared in 1992
255 in binary is 11111111, octal 377, hex ff
First letter: D
Primes: #(2, 3, 5, 7, 11)
Test coverage: 100%
Key Concepts
- Everything is a stream — the console (
*standard-input*,*standard-output*) and files are all stream objects, so the same functions (read-line,write-line,read-to-end) work across them. format-outis Dylan’sprintf— directives like%s,%d,%b,%o,%x,%c, and%=format values, and%%produces a literal percent sign.%=prints any object’s inspect representation, making it the go-to directive for displaying collections such as lists and vectors.- The module system gates I/O — a source file must
use streams;anduse standard-io;to reach input and file operations, which is why they need a fuller library definition than a bareformat-outprogram. with-open-fileopens and reliably closes a file stream, and returns the value of its body — even if an error is signalled inside it.read-linewithon-end-of-stream:returns a sentinel (often#f) at end of file instead of raising an error, which makes line-by-line loops straightforward.- Input is text —
read-lineyields a<string>, so use converters likestring-to-integerbefore doing arithmetic on user input. force-outputflushes buffered output, ensuring prompts appear before the program blocks waiting for input.
Running Today
All examples can be run using Docker:
docker pull codearchaeology/dylan:latest
Comments
Loading comments...
Leave a Comment