Most I/O code only operates on one stream at a time. The input-stream and output-stream variables are implicit parameters used by many I/O words. Using this idiom improves code in three ways:
•
Code becomes simpler because there is no need to keep a stream around on the stack.
Code becomes more reusable because it can be written to not worry about which stream is being used, and instead the caller can use with-input-stream or with-output-stream to specify the source or destination for I/O operations.
For example, here is a program which reads the first line of a file, converts it to an integer, then reads that many characters, and splits them into groups of 16:
USING: continuations kernel io io.files math.parser splitting ;
"data.txt" utf8 <file-reader>
dup stream-readln string>number over stream-read 16 group
swap dispose
This code has two problems: it has some unnecessary stack shuffling, and if either stream-readln or stream-read throws an I/O error, the stream is not closed because dispose is never reached. So we can add a call to with-disposal to ensure the stream is always closed:
This code is robust, however it is more complex than it needs to be. This is where the default stream words come in; using them, the above can be rewritten as follows: