Data & I/O
Streaming files & URLs
`StreamFile(path)` and `StreamURL(url)` (§ StreamFile/StreamURL polymorphic streams, see docs/qu-language-tour.md §23) are two different constructors that expose the SAME method names -- `read_line`/`read_all`/`eof`/`close` -- so one
catalog/qu_streamfile_streamurl.qu
function written against "a stream" works completely unmodified against either kind. `StreamFile` is literally `fopen`'s own machinery under a second name (a real `Value::File` handle); `StreamURL` makes a real HTTP GET (the same request `http_get` makes, see qu_http_get.qu) and wraps the whole body eagerly in a read-only in-memory cursor answering to the exact same method names.
This demo proves the polymorphism for real: the exact same `drain` function below is called once against a real local file and once against a real network response, with zero branching on which kind it received. The network half is wrapped in try/catch, same honest pattern as qu_http_get.qu -- on a machine with no outbound network access this prints a clear "network unavailable" message instead of crashing, while the StreamFile half (pure local disk I/O) always runs.
What it exercises
- StreamFile(path) -- fopen's own machinery under a second name
- StreamURL(url) -- eager real HTTP GET wrapped as a read-only stream
- read_line(s) / eof(s) / close(s) -- the shared method surface both kinds answer to
- try/catch -- graceful handling of the real network dependency
How it works
One function, written against "a stream" -- no branching on the
concrete kind of `s` anywhere in this body.
function drain(s)
total = ""
n = 0
while not eof(s)
line = read_line(s)
total = total + line + "|"
n = n + 1
end while
close(s)
return total
end function
StreamFile side: a real local file with three known lines
notes_path = tmp_file()
w = fopen(notes_path, "w")
write_line(w, "alpha")
write_line(w, "beta")
write_line(w, "gamma")
close(w)
a = drain(StreamFile(notes_path))
print("StreamFile drain result: \"{a}\"")
StreamURL side: a real HTTP GET against the same stable target
qu_http_get.qu uses, drained through the IDENTICAL `drain` function above
try
b = drain(StreamURL("https://example.com/"))
print("StreamURL drain length: {length(b)} chars")
print("both streams answered to read_line/eof/close with zero branching in `drain`")
catch err
print("StreamURL could not reach the network: {err.message}")
print("this is the expected, honest result in an environment with no outbound network access")
print("(the StreamFile half above already proved the shared interface on its own)")
end
Results
StreamFile drain result: "alpha|beta|gamma|" StreamURL drain length: 559 chars both streams answered to read_line/eof/close with zero branching in `drain`
The whole program
Runnable as it stands: qu run catalog/qu_streamfile_streamurl.qu
# qu_streamfile_streamurl.qu -- `StreamFile(path)` and `StreamURL(url)`
# (§ StreamFile/StreamURL polymorphic streams, see
# docs/qu-language-tour.md §23) are two different constructors that expose
# the SAME method names -- `read_line`/`read_all`/`eof`/`close` -- so one
# function written against "a stream" works completely unmodified against
# either kind. `StreamFile` is literally `fopen`'s own machinery under a
# second name (a real `Value::File` handle); `StreamURL` makes a real HTTP
# GET (the same request `http_get` makes, see qu_http_get.qu) and wraps the
# whole body eagerly in a read-only in-memory cursor answering to the exact
# same method names.
#
# This demo proves the polymorphism for real: the exact same `drain`
# function below is called once against a real local file and once against
# a real network response, with zero branching on which kind it received.
# The network half is wrapped in try/catch, same honest pattern as
# qu_http_get.qu -- on a machine with no outbound network access this
# prints a clear "network unavailable" message instead of crashing, while
# the StreamFile half (pure local disk I/O) always runs.
#
# Constructs exercised (all real, verified-working builtins):
# StreamFile(path) -- fopen's own machinery under a second name
# StreamURL(url) -- eager real HTTP GET wrapped as a read-only stream
# read_line(s) / eof(s) / close(s) -- the shared method surface both kinds answer to
# try/catch -- graceful handling of the real network dependency
backend auto
#%% One function, written against "a stream" -- no branching on the
# concrete kind of `s` anywhere in this body.
function drain(s)
total = ""
n = 0
while not eof(s)
line = read_line(s)
total = total + line + "|"
n = n + 1
end while
close(s)
return total
end function
#%% StreamFile side: a real local file with three known lines
notes_path = tmp_file()
w = fopen(notes_path, "w")
write_line(w, "alpha")
write_line(w, "beta")
write_line(w, "gamma")
close(w)
a = drain(StreamFile(notes_path))
print("StreamFile drain result: \"{a}\"")
#%% StreamURL side: a real HTTP GET against the same stable target
# qu_http_get.qu uses, drained through the IDENTICAL `drain` function above
try
b = drain(StreamURL("https://example.com/"))
print("StreamURL drain length: {length(b)} chars")
print("both streams answered to read_line/eof/close with zero branching in `drain`")
catch err
print("StreamURL could not reach the network: {err.message}")
print("this is the expected, honest result in an environment with no outbound network access")
print("(the StreamFile half above already proved the shared interface on its own)")
end