]> gitweb.factorcode.org Git - factor.git/blob - core/io/io-docs.factor
c9cb248925ee652a03d827c927c07051b1454cd2
[factor.git] / core / io / io-docs.factor
1 USING: help.markup help.syntax quotations hashtables kernel
2 classes strings continuations destructors math byte-arrays
3 alien specialized-arrays sequences ;
4 IN: io
5
6 ARTICLE: "stream-types" "Binary and text streams"
7 "A word which outputs the stream element type:"
8 { $subsections stream-element-type }
9 "Stream element types:"
10 { $subsections +byte+ +character+ }
11 "The stream element type is the data type read and written by " { $link stream-read1 } " and " { $link stream-write1 } "."
12 $nl
13 "Binary streams have an element type of " { $link +byte+ } ". Elements are integers in the range " { $snippet "[0,255]" } ", representing bytes. Reading a sequence of elements produces a " { $link byte-array } ". Any object implementing the " { $link >c-ptr } " and " { $link byte-length } " generic words can be written to a binary stream."
14 $nl
15 "Character streams have an element type of " { $link +character+ } ". Elements are non-negative integers, representing Unicode code points. Only instances of the " { $link string } " class can be read or written on a character stream."
16 $nl
17 "Most external streams are binary streams, and can be wrapped in string streams once a suitable encoding has been provided; see " { $link "io.encodings" } "." ;
18
19 HELP: +byte+
20 { $description "A stream element type. See " { $link stream-element-type } " for explanation." } ;
21
22 HELP: +character+
23 { $description "A stream element type. See " { $link stream-element-type } " for explanation." } ;
24
25 HELP: stream-element-type
26 { $values { "stream" "a stream" } { "type" { $link +byte+ } " or " { $link +character+ } } }
27 { $contract "Outputs one of " { $link +byte+ } " or " { $link +character+ } "." } ;
28
29 HELP: stream-readln
30 { $values { "stream" "an input stream" } { "str/f" { $maybe string } } }
31 { $contract "Reads a line of input from the stream. Outputs " { $link f } " on stream exhaustion." }
32 { $notes "Most code only works on one stream at a time and should instead use " { $link readln } "; see " { $link "stdio" } "." }
33 $io-error ;
34
35 HELP: stream-read1
36 { $values { "stream" "an input stream" } { "elt" { $maybe "an element" } } }
37 { $contract "Reads an element from the stream. Outputs " { $link f } " on stream exhaustion." }
38 { $notes "Most code only works on one stream at a time and should instead use " { $link read1 } "; see " { $link "stdio" } "." }
39 $io-error ;
40
41 HELP: stream-read
42 { $values { "n" "a non-negative integer" } { "stream" "an input stream" } { "seq/f" { $or byte-array string f } } }
43 { $contract "Reads " { $snippet "n" } " elements from the stream. Outputs a truncated string or " { $link f } " on stream exhaustion." }
44 { $notes "Most code only works on one stream at a time and should instead use " { $link read } "; see " { $link "stdio" } "." }
45 $io-error ;
46
47 HELP: stream-read-unsafe
48 { $values { "n" "a non-negative integer" } { "buf" { $or c-ptr string } } { "stream" "an input stream" } { "count" integer } }
49 { $contract "Reads up to " { $snippet "n" } " elements from the stream. The data is stored directly into the buffer provided by " { $snippet "buf" } ", which must be a string (in the case of a character stream) or a byte array, specialized array, or other pointer to memory (in the case of a byte stream). There must be space in the buffer for at least " { $snippet "n" } " elements. Returns the number of elements read from the stream, which will be equal to " { $snippet "n" } " unless the end of the stream is reached. If the stream is exhausted, returns zero." }
50 { $warning "This word does not perform bounds checking on " { $snippet "buf" } ". Most code should use " { $link stream-read } " or " { $link stream-read-into } " instead." }
51 $io-error ;
52
53 HELP: read-into
54 { $values { "buf" { $or byte-array specialized-array string } } { "buf-slice" slice } { "more?" boolean } }
55 { $contract "Reads from the current " { $link input-stream } " into the sequence " { $snippet "buf" } ", until either the length of " { $snippet "buf" } " is reached or the stream is exhausted. Returns a " { $link slice } " over the part of " { $snippet "buf" } " that was written to, and a boolean value that will be " { $link f } " if the stream was exhausted." }
56 $io-error ;
57
58 HELP: stream-read-into
59 { $values { "buf" { $or byte-array specialized-array string } } { "stream" "an input stream" } { "buf-slice" slice } { "more?" boolean } }
60 { $contract "Reads from the stream into the sequence " { $snippet "buf" } ", until either the length of " { $snippet "buf" } " is reached or the stream is exhausted. Returns a " { $link slice } " over the part of " { $snippet "buf" } " that was written to, and a boolean value that will be " { $link f } " if the stream was exhausted." }
61 { $notes "Most code only works on one stream at a time and should instead use " { $link read-into } "; see " { $link "stdio" } "." }
62 $io-error ;
63
64 HELP: stream-read-until
65 { $values { "seps" string } { "stream" "an input stream" } { "seq" { $or byte-array string f } } { "sep/f" { $maybe "a character" } } }
66 { $contract "Reads elements from the stream, until the first occurrence of a separator character, or stream exhaustion. In the former case, the separator is pushed on the stack, and is not part of the output string. In the latter case, the entire stream contents are output, along with " { $link f } "." }
67 { $notes "Most code only works on one stream at a time and should instead use " { $link read-until } "; see " { $link "stdio" } "." }
68 $io-error ;
69
70 HELP: stream-read-partial
71 { $values
72      { "n" "a non-negative integer" } { "stream" "an input stream" }
73      { "seq/f" { $or byte-array string f } } }
74 { $description "Reads at most " { $snippet "n" } " elements from a stream and returns up to that many characters without blocking. If no characters are available, blocks until some are and returns them." } ;
75
76 HELP: stream-read-partial-unsafe
77 { $values { "n" "a non-negative integer" } { "buf" { $or c-ptr string } } { "stream" "an input stream" } { "count" integer } }
78 { $contract "Reads up to " { $snippet "n" } " elements from the stream without blocking. If no data is available immediately on the stream, blocks until data is available. The data is stored directly into the buffer provided by " { $snippet "buf" } ", which must be a string (in the case of a character stream), or a byte array, specialized array, or other pointer to memory (in the case of a byte stream). There must be space in the buffer for at least " { $snippet "n" } " elements. Returns the number of elements read from the stream, or zero if the end of the stream was reached." }
79 { $warning "This word does not perform bounds checking on " { $snippet "buf" } ". Most code should use " { $link stream-read-partial } " or " { $link stream-read-partial-into } " instead." }
80 $io-error ;
81
82 HELP: read-partial-into
83 { $values { "buf" { $or byte-array specialized-array string } } { "buf-slice" slice } { "more?" boolean } }
84 { $contract "Reads available data from the current " { $link input-stream } " into the sequence " { $snippet "buf" } " without blocking until all immediately available data is read or the length of " { $snippet "buf" } " is reached. If no data is immediately available, blocks until data is available. Returns a " { $link slice } " over the part of " { $snippet "buf" } " that was written to, and a boolean that will be " { $link f } " if the stream was exhausted." }
85 $io-error ;
86
87 HELP: stream-read-partial-into
88 { $values { "buf" { $or byte-array specialized-array string } } { "stream" "an input stream" } { "buf-slice" slice } { "more?" boolean } }
89 { $contract "Reads available data from the stream into the sequence " { $snippet "buf" } " without blocking until all immediately available data is read or the length of " { $snippet "buf" } " is reached. If no data is immediately available, blocks until data is available. Returns a " { $link slice } " over the part of " { $snippet "buf" } " that was written to, and a boolean that will be " { $link f } " if the stream was exhausted." }
90 { $notes "Most code only works on one stream at a time and should instead use " { $link read-partial-into } "; see " { $link "stdio" } "." }
91 $io-error ;
92
93 HELP: stream-write1
94 { $values { "elt" "an element" } { "stream" "an output stream" } }
95 { $contract "Writes an element to the stream. If the stream does buffering, output may not be performed immediately; use " { $link stream-flush } " to force output." }
96 { $notes "Most code only works on one stream at a time and should instead use " { $link write1 } "; see " { $link "stdio" } "." }
97 $io-error ;
98
99 HELP: stream-write
100 { $values { "data" "binary data or a string" } { "stream" "an output stream" } }
101 { $contract "Writes a piece of data to the stream. If the stream performs buffering, output may not be performed immediately; use " { $link stream-flush } " to force output." }
102 { $notes "Most code only works on one stream at a time and should instead use " { $link write } "; see " { $link "stdio" } "." }
103 $io-error ;
104
105 HELP: stream-flush
106 { $values { "stream" "an output stream" } }
107 { $contract "Waits for any pending output to complete." }
108 { $notes "With many output streams, written output is buffered and not sent to the underlying resource until either the buffer is full, or this word is called." }
109 { $notes "Most code only works on one stream at a time and should instead use " { $link flush } "; see " { $link "stdio" } "." }
110 $io-error ;
111
112 HELP: stream-nl
113 { $values { "stream" "an output stream" } }
114 { $contract "Writes a line terminator. If the stream does buffering, output may not be performed immediately; use " { $link stream-flush } " to force output." }
115 { $notes "Most code only works on one stream at a time and should instead use " { $link nl } "; see " { $link "stdio" } "." }
116 $io-error ;
117
118 HELP: stream-print
119 { $values { "str" string } { "stream" "an output stream" } }
120 { $description "Writes a newline-terminated string." }
121 { $notes "Most code only works on one stream at a time and should instead use " { $link print } "; see " { $link "stdio" } "." }
122 $io-error ;
123
124 HELP: stream-copy*
125 { $values { "in" "an input stream" } { "out" "an output stream" } }
126 { $description "Copies the contents of one stream into another. The streams are left open when the copy is completed. The " { $link stream-copy } " word can be used instead to close them on completion, if desired." }
127 $io-error ;
128
129 HELP: stream-copy
130 { $values { "in" "an input stream" } { "out" "an output stream" } }
131 { $description "Copies the contents of one stream into another, closing both streams when done. To copy without closing the streams, use " { $link stream-copy* } "." }
132 $io-error ;
133
134 HELP: stream-tell
135 { $values
136      { "stream" "a stream" } { "n" integer }
137 }
138 { $description "Returns the index of the stream pointer if the stream is seekable." }
139 { $notes "Stream seeking is not supported on streams that do not have a known length, e.g. TCP/IP streams." } ;
140
141 HELP: stream-seek
142 { $values
143      { "n" integer } { "seek-type" "a seek singleton" } { "stream" "a stream" }
144 }
145 { $description "Moves the pointer associated with a stream's handle to an offset " { $snippet "n" } " bytes from the seek type so that further reading or writing happens at the new location. For output streams, the buffer is flushed before seeking. Seeking past the end of an output stream will pad the difference with zeros once the stream is written to again." $nl
146     "Three methods of seeking are supported:"
147     { $list { $link seek-absolute } { $link seek-relative } { $link seek-end } }
148 }
149 { $notes "Stream seeking is not supported on streams that do not have a known length, e.g. TCP/IP streams." } ;
150
151 HELP: stream-seekable?
152 { $values
153      { "stream" "a stream" } { "?" boolean }
154 }
155 { $description "Returns true if " { $snippet "stream" } " is a seekable stream." }
156 { $notes "Stream seeking is not supported on streams that do not have a known length, e.g. TCP/IP streams." } ;
157
158 HELP: stream-length
159 { $values
160      { "stream" "a stream" } { "n/f" { $maybe integer } }
161 }
162 { $description "Returns the length of the data supplied by " { $snippet "stream" } ", or " { $link f } " if the stream is not seekable or has unknown length." }
163 { $notes "Stream seeking is not supported on streams that do not have a known length, e.g. TCP/IP streams." } ;
164
165 HELP: seek-absolute
166 { $values
167
168      { "value" "a seek singleton" }
169 }
170 { $description "Seeks to an offset from the beginning of the stream." } ;
171
172 HELP: seek-end
173 { $values
174
175      { "value" "a seek singleton" }
176 }
177 { $description "Seeks to an offset from the end of the stream. If the offset puts the stream pointer past the end of the data on an output stream, writing to it will pad the difference with zeros." } ;
178
179 HELP: seek-relative
180 { $values
181
182      { "value" "a seek singleton" }
183 }
184 { $description "Seeks to an offset from the current position of the stream pointer." } ;
185
186 { stream-seek stream-tell stream-seekable? stream-length } related-words
187 { seek-absolute seek-relative seek-end } related-words
188
189 HELP: seek-input
190 { $values
191      { "n" integer } { "seek-type" "a seek singleton" }
192 }
193 { $description "Calls " { $link stream-seek } " on the stream stored in " { $link input-stream } "." } ;
194
195 HELP: seek-output
196 { $values
197      { "n" integer } { "seek-type" "a seek singleton" }
198 }
199 { $description "Calls " { $link stream-seek } " on the stream stored in " { $link output-stream } "." } ;
200
201 HELP: input-stream
202 { $var-description "Holds an input stream for various implicit stream operations. Rebound using " { $link with-input-stream } " and " { $link with-input-stream* } "." } ;
203
204 HELP: output-stream
205 { $var-description "Holds an output stream for various implicit stream operations. Rebound using " { $link with-output-stream } " and " { $link with-output-stream* } "." } ;
206
207 HELP: error-stream
208 { $var-description "Holds an error stream." } ;
209
210 HELP: readln
211 { $values { "str/f" { $maybe string } } }
212 { $description "Reads a line of input from " { $link input-stream } ". Outputs " { $link f } " on stream exhaustion." }
213 $io-error ;
214
215 HELP: read1
216 { $values { "elt" { $maybe "an element" } } }
217 { $description "Reads an element from " { $link input-stream } ". Outputs " { $link f } " on stream exhaustion." }
218 $io-error ;
219
220 HELP: read
221 { $values { "n" "a non-negative integer" } { "seq" { $or byte-array string f } } }
222 { $description "Reads " { $snippet "n" } " elements from " { $link input-stream } ". If there is no input available, outputs " { $link f } ". If there are less than " { $snippet "n" } " elements available, outputs a sequence shorter than " { $snippet "n" } " in length." }
223 $io-error ;
224
225 HELP: read-until
226 { $values { "seps" string } { "seq" { $or byte-array string f } } { "sep/f" { $maybe "a character" } } }
227 { $contract "Reads elements from " { $link input-stream } " until the first occurrence of a separator, or stream exhaustion. In the former case, the separator character is pushed on the stack, and is not part of the output. In the latter case, the entire stream contents are output, along with " { $link f } "." }
228 $io-error ;
229
230 HELP: read-partial
231 { $values { "n" integer } { "seq" { $or byte-array string f } } }
232 { $description "Reads at most " { $snippet "n" } " elements from " { $link input-stream } " and returns them in a sequence. This word should be used instead of " { $link read } " when processing the entire element a chunk at a time, since on some stream implementations it may be slightly faster." } ;
233
234 HELP: write1
235 { $values { "elt" "an element" } }
236 { $contract "Writes an element to " { $link output-stream } ". If the stream does buffering, output may not be performed immediately; use " { $link flush } " to force output." }
237 $io-error ;
238
239 HELP: write
240 { $values { "seq" { $or byte-array string f } } }
241 { $description "Writes a sequence of elements to " { $link output-stream } ". If the stream does buffering, output may not be performed immediately; use " { $link flush } " to force output." }
242 $io-error ;
243
244 HELP: flush
245 { $description "Waits for any pending output on " { $link output-stream } " to complete." }
246 $io-error ;
247
248 HELP: nl
249 { $description "Writes a line terminator to " { $link output-stream } ". If the stream does buffering, output may not be performed immediately; use " { $link flush } " to force output." }
250 $io-error ;
251
252 HELP: print
253 { $values { "str" string } }
254 { $description "Writes a newline-terminated string to " { $link output-stream } "." }
255 $io-error ;
256
257 HELP: with-input-stream
258 { $values { "stream" "an input stream" } { "quot" quotation } }
259 { $description "Calls the quotation in a new dynamic scope, with " { $link input-stream } " rebound to " { $snippet "stream" } ". The stream is closed if the quotation returns or throws an error." } ;
260
261 HELP: with-output-stream
262 { $values { "stream" "an output stream" } { "quot" quotation } }
263 { $description "Calls the quotation in a new dynamic scope, with " { $link output-stream } " rebound to " { $snippet "stream" } ". The stream is closed if the quotation returns or throws an error." } ;
264
265 HELP: with-streams
266 { $values { "input" "an input stream" } { "output" "an output stream" } { "quot" quotation } }
267 { $description "Calls the quotation in a new dynamic scope, with " { $link input-stream } " rebound to " { $snippet "input" } " and " { $link output-stream } " rebound to " { $snippet "output" } ". The streams are closed if the quotation returns or throws an error." } ;
268
269 HELP: with-streams*
270 { $values { "input" "an input stream" } { "output" "an output stream" } { "quot" quotation } }
271 { $description "Calls the quotation in a new dynamic scope, with " { $link input-stream } " rebound to " { $snippet "input" } " and " { $link output-stream } " rebound to " { $snippet "output" } "." }
272 { $notes "This word does not close the streams. Compare with " { $link with-streams } "." } ;
273
274 { with-input-stream with-input-stream* } related-words
275
276 { with-output-stream with-output-stream* } related-words
277
278 HELP: with-input-stream*
279 { $values { "stream" "an input stream" } { "quot" quotation } }
280 { $description "Calls the quotation in a new dynamic scope, with " { $link input-stream } " rebound to " { $snippet "stream" } "." }
281 { $notes "This word does not close the stream. Compare with " { $link with-input-stream } "." } ;
282
283 HELP: with-output-stream*
284 { $values { "stream" "an output stream" } { "quot" quotation } }
285 { $description "Calls the quotation in a new dynamic scope, with " { $link output-stream } " rebound to " { $snippet "stream" } "." }
286 { $examples
287   { $unchecked-example
288     "USING: destructors io io.encodings.utf8 io.files prettyprint ;"
289     "\"/tmp/test.txt\" utf8 <file-writer> dup [ \"Hello!\" write ] with-output-stream* dispose"
290     "\"/tmp/test.txt\" utf8 file-contents ."
291     "\"Hello!\""
292   }
293 }
294 { $notes "This word does not close the stream. Compare with " { $link with-output-stream } "." } ;
295
296 HELP: bl
297 { $description "Outputs a space character (" { $snippet "\" \"" } ") to " { $link output-stream } "." }
298 $io-error ;
299
300 HELP: stream-lines
301 { $values { "stream" "an input stream" } { "seq" { $sequence string } } }
302 { $description "Reads lines of text until the stream is exhausted, collecting them in a sequence of strings." } ;
303
304 HELP: lines
305 { $values { "seq" { $sequence string } } }
306 { $description "Reads lines of text until from the " { $link input-stream } " until it is exhausted, collecting them in a sequence of strings." } ;
307
308 HELP: each-line
309 { $values { "quot" { $quotation ( ... line -- ... ) } } }
310 { $description "Calls the quotation with successive lines of text, until the current " { $link input-stream } " is exhausted." } ;
311
312 HELP: each-block
313 { $values { "quot" { $quotation ( ... block -- ... ) } } }
314 { $description "Calls the quotation with successive blocks of data, until the current " { $link input-stream } " is exhausted." } ;
315
316 HELP: stream-contents
317 { $values { "stream" "an input stream" } { "seq" { $or string byte-array } } }
318 { $description "Reads all elements in the given stream until the stream is exhausted. The type of the sequence depends on the stream's element type. The stream is closed after completion." }
319 $io-error ;
320
321 HELP: contents
322 { $values { "seq" { $or string byte-array } } }
323 { $description "Reads all elements in the " { $link input-stream } " until the stream is exhausted. The type of the sequence depends on the stream's element type." }
324 $io-error ;
325
326 HELP: tell-input
327 { $values
328         { "n" integer }
329 }
330 { $description "Returns the index of the stream stored in " { $link input-stream } "." } ;
331
332 HELP: tell-output
333 { $values
334         { "n" integer }
335 }
336 { $description "Returns the index of the stream stored in " { $link output-stream } "." } ;
337
338 ARTICLE: "stream-protocol" "Stream protocol"
339 "The stream protocol consists of a large number of generic words, many of which are optional."
340 $nl
341 "Stream protocol words are rarely called directly, since code which only works with one stream at a time should be written to use " { $link "stdio" } " instead, wrapping I/O operations such as " { $link read } " and " { $link write } " in " { $link with-input-stream } " and " { $link with-output-stream } "."
342 $nl
343 "All streams must implement the " { $link dispose } " word in addition to the stream protocol."
344 { $subsections "stream-types" }
345 "These words are required for binary and string input streams:"
346 { $subsections
347     stream-read1
348     stream-read-unsafe
349     stream-read-until
350     stream-read-partial-unsafe
351 }
352 "The " { $link stream-read-unsafe } " and " { $link stream-read-partial-unsafe } " words should be implemented by streams but not used by client code. The following safe words are provided for reading from input streams:"
353 { $subsections
354     stream-read
355     stream-read-into
356     stream-read-partial
357     stream-read-partial-into
358 }
359 "This word is only required for string input streams:"
360 { $subsections stream-readln }
361 "These words are required for binary and string output streams:"
362 { $subsections
363     stream-flush
364     stream-write1
365     stream-write
366 }
367 "This word is only required for string output streams:"
368 { $subsections stream-nl }
369 "These words are for seekable streams:"
370 { $subsections
371     stream-seekable?
372     stream-tell
373     stream-seek
374     tell-input
375     tell-output
376     stream-length
377 }
378 { $see-also "io.timeouts" } ;
379
380 ARTICLE: "stdio-motivation" "Motivation for default streams"
381 "Most I/O code only operates on one stream at a time. The " { $link input-stream } " and " { $link output-stream } " variables are implicit parameters used by many I/O words. Using this idiom improves code in three ways:"
382 { $list
383     { "Code becomes simpler because there is no need to keep a stream around on the stack." }
384     { "Code becomes more robust because " { $link with-input-stream } " and " { $link with-output-stream } " automatically close the streams if there is an error." }
385     { "Code becomes more reusable because it can be written to not worry about which stream is being used, and instead the caller can use " { $link with-input-stream } " or " { $link with-output-stream } " to specify the source or destination for I/O operations." }
386 }
387 "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:"
388 { $code
389     "USING: continuations kernel io io.files math.parser splitting ;"
390     "\"data.txt\" utf8 <file-reader>"
391     "dup stream-readln string>number over stream-read 16 group"
392     "swap dispose"
393 }
394 "This code has two problems: it has some unnecessary stack shuffling, and if either " { $link stream-readln } " or " { $link stream-read } " throws an I/O error, the stream is not closed because " { $link dispose } " is never reached. So we can add a call to " { $link with-disposal } " to ensure the stream is always closed:"
395 { $code
396     "USING: continuations kernel io io.files math.parser splitting ;"
397     "\"data.txt\" utf8 <file-reader> ["
398     "    dup stream-readln string>number over stream-read"
399     "    16 group"
400     "] with-disposal"
401 }
402 "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:"
403 { $code
404     "USING: continuations kernel io io.files math.parser splitting ;"
405     "\"data.txt\" utf8 <file-reader> ["
406     "    readln string>number read 16 group"
407     "] with-input-stream"
408 }
409 "An even better implementation that takes advantage of a utility word:"
410 { $code
411     "USING: continuations kernel io io.files math.parser splitting ;"
412     "\"data.txt\" utf8 ["
413     "    readln string>number read 16 group"
414     "] with-file-reader"
415 } ;
416
417 ARTICLE: "stdio" "Default input and output streams"
418 { $subsections "stdio-motivation" }
419 "The default input stream is stored in a dynamically-scoped variable:"
420 { $subsections input-stream }
421 "Unless rebound in a child namespace, this variable will be set to a console stream for reading input from the user."
422 $nl
423 "Words reading from the default input stream:"
424 { $subsections
425     read1
426     read
427     read-into
428     read-until
429     read-partial
430     read-partial-into
431     readln
432 }
433 "If the default input stream is a character stream (" { $link stream-element-type } " outputs " { $link +character+ } "), lines of text can be read:"
434 { $subsections readln }
435 "Seeking on the default input stream:"
436 { $subsections seek-input }
437 "A pair of combinators for rebinding the " { $link input-stream } " variable:"
438 { $subsections
439     with-input-stream
440     with-input-stream*
441 }
442 "The default output stream is stored in a dynamically-scoped variable:"
443 { $subsections output-stream }
444 "Unless rebound in a child namespace, this variable will be set to a console stream for showing output to the user."
445 $nl
446 "Words writing to the default output stream:"
447 { $subsections
448     flush
449     write1
450     write
451 }
452 "If the default output stream is a character stream (" { $link stream-element-type } " outputs " { $link +character+ } "), lines of text can be written:"
453 { $subsections
454     print
455     nl
456     bl
457 }
458 "Seeking on the default output stream:"
459 { $subsections seek-output }
460 "A pair of combinators for rebinding the " { $link output-stream } " variable:"
461 { $subsections
462     with-output-stream
463     with-output-stream*
464 }
465 "A pair of combinators for rebinding both default streams at once:"
466 { $subsections
467     with-streams
468     with-streams*
469 } ;
470
471 ARTICLE: "stream-utils" "Stream utilities"
472 "There are a few useful stream-related words which are not generic, but merely built up from the stream protocol."
473 $nl
474 "First, a simple composition of " { $link stream-write } " and " { $link stream-nl } ":"
475 { $subsections stream-print }
476 "Processing lines one by one:"
477 { $subsections
478     stream-lines
479     lines
480     each-line
481 }
482 "Processing blocks of data:"
483 { $subsections
484     stream-contents
485     contents
486     each-block
487 }
488 "Copying the contents of one stream to another:"
489 { $subsections
490     stream-copy*
491     stream-copy
492 } ;
493
494 ARTICLE: "stream-examples" "Stream example"
495 "Ask the user for their age, and print it back:"
496 { $code
497     "USING: io math.parser ;"
498     ""
499     ": ask-age ( -- ) \"How old are you?\" print ;"
500     ""
501     ": read-age ( -- n ) readln string>number ;"
502     ""
503     ": print-age ( n -- )"
504     "    \"You are \" write"
505     "    number>string write"
506     "    \" years old.\" print ;"
507     ": example ( -- ) ask-age read-age print-age ;"
508     ""
509     "example"
510 } ;
511
512 ARTICLE: "streams" "Streams"
513 "Input and output centers on the concept of a " { $emphasis "stream" } ", which is a source or sink of " { $emphasis "elements" } "."
514 { $subsections "stream-examples" }
515 "A stream can either be passed around on the stack or bound to a dynamic variable and used as one of the two implicit " { $emphasis "default streams" } "."
516 { $subsections
517     "stream-protocol"
518     "stdio"
519     "stream-utils"
520 }
521 { $see-also "io.streams.string" "io.streams.plain" "io.streams.duplex" } ;
522
523 ABOUT: "streams"