|
Though InStream has many methods, it has only a
few primitives. The primitive used for obtaining elements from the stream
is:
public Object obtain(int atLeast,
int atMost,
String sched,
String proceed,
String report)
throws UnavailableException,
IOException
Most of the other InStream methods are just convenient repackagings
of this primitive. This primitive and all its packagings are collectively
refered to as input operations.
The values of four of these parameters -- atLeast,
sched, proceed, and report -- form a four-dimensional
2*2*3*2 taxonomy of input operations, visualized in the following two
tables.
proceed * report
The main taxonomy is formed by the proceed and report
parameters. Each has only two possible values, shown as the row and column
labels, respectively.
(At first I defined these as boolean parameters, but looking at "..,true,false)"
in an argument list is much less clear than looking at "..,ADVANCE,STATUS)".)
The proceed parameter says whether
-
ADVANCE - The stream should be advanced past the
obtained elements, relieving backpressure and allowing further elements
to flow downstream.
-
QUERY - The input operation should leave the stream
unaffected so that these elements may be re-obtained.
The report parameter says what the operation should report as
its result:
-
ELEMENTS - A list of the elements obtained. If at
end of stream, an empty list is returned rather than null.
-
STATUS - The termination status of the stream after
these elements have been obtained. When asked to report STATUS,
the obtained elements are ignored.
Crossing these, we get:
-
reading operations - that report the elements obtained,
and advance the stream past these elements, thereby consuming these
elements from the stream. (A more suggestive name would have been
"taking" (and "take" for the corresponding
method names), but "reading" and "read"
is the conventional name for these.)
-
skipping operations - That advance the stream past
a certain number of elements without reporting them, thereby consuming
them without making them accessible.
-
peeking operations - That report the next elements
from the stream without consuming them.
-
checking operations - For checking what the stream's
termination status will be after a certain number of elements have
been consumed.
The row and column labels on the next table show our two remaining dimensions.
In order to show the complete taxonomy, in each of the resulting 3*2 cells
we show the above 2*2 table, giving us a taxonomy of 24 possible input
operations. Although all are well defined, only a few will normally be
used. For these, we have defined convenience methods that are defined
by their expansion to obtain/5.
sched * atLeast
The sched parameter says how the operation should be scheduled:
-
NOW - The operation is performed immediately, whether
or not sufficient elements are ready.
If a sufficient number of elements cannot be obtained immediately,
then an UnavailableException or other IOException
is thrown. If an UnavailableException is thrown, then nothing
was consumed and no side-effects should have happened. If another
IOException is thrown, then any number up to atMost
may have been consumed.
-
WAIT - Warning: may block the vat and cause deadlock.
If the operation can succeed immediately, it is performed immediately.
Else, if insufficient elements are currently ready, and if this InStream
supports waiting, then the calling vat/runner/thread is blocked until
this operation can immediately complete. The wrappers of java.io
streams support waiting.
This should normally only be used when
-
The programmer knows the source to be prompt even though the
underlying java.io stream doesn't.
-
This vat (and its runner) exists for the purpose of building
one virtual device from some underlying non-prompt stream,
as with a one-top-level-expr-at-a-time parser parsing an InStream
of characters.
-
LATER - Registers a claim for the next atLeast..atMost elements, and returns a promise for them. When these characters become ready, the claim will be satisfied and the promise will be resolved.
An input operation must obtain a sufficient number of elements.
The atLeast and atMost parameters express bounds on
the number of elements that are considered sufficient. Both atLeast
and atMost may be ALL
(ie, Integer.MAX_VALUE),
or may be any non-negative integer between 0 and ALL (a quantity).
When atLeast is
-
ALL - then a sufficient number of elements
means all the remaining elements that will ever appear on the stream,
from the next element until stream termination.
-
a quantity - then a sufficient number means
at least this many, or all the remaining elements on the stream, whichever
comes first.
atMost must be >= atLeast. When atLeast
is ALL atMost must also be ALL. A sufficient
number never exceeds atMost.
Crossing all these dimensions, we get the full taxonomy, each element
of which is represented by a cell in the above table. The labels are the
convenience methods we've provided for normal operation, defined by the
following expansions:
Convenience |
Expansion |
read(atLeast,atMost)
|
obtain(atLeast,atMost,NOW,ADVANCE,ELEMENTS)
|
If a sufficient number of elements are ready NOW, obtain
atLeast that many , but not more than atMost,
elements, ADVANCE the stream past them, and return a list
of those ELEMENTS.
If a sufficient number aren't ready NOW, complain and
read nothing.
|
readReady(num)
|
read(0,num)
|
read as many elements as are ready NOW, but
not more than num.
|
readReady()
|
read(0,ALL)
|
read as many elements as are ready NOW.
|
readOptOne()
|
switch (read(1,1)) {
match [] { null }
match [x] { x }
}
|
If the stream is terminated, return null. Else return
the next element. For streams than may contain null elements,
readOptOne should probably be avoided, or the caller must be careful
to deal with the resulting ambiguity -- the two reasons why a null
might be returned.
If the stream isn't yet known to be terminated, and the next
element is not yet ready, then complain and return nothing.
|
readAll()
|
read(ALL,ALL)
|
read ALL the remaining elements NOW,
which must therefore all be ready now.
|
peek(num)
|
obtain(num,num,NOW,QUERY,ELEMENTS)
|
obtain num elements (or all remaining elements,
whichever comes first) NOW, as a QUERY (does not
ADVANCE the stream), and return a list of these ELEMENTS.
|
readLater(atLeast,atMost)
|
obtain(atLeast,atMost,LATER,ADVANCE,ELEMENTS)
|
Claim the next sufficient number of elements (according to atLeast..atMost),
to be obtained at a LATER time, once they are
ready, and ADVANCE the stream past this claim. Return a
vow that will resolve to a list of these ELEMENTS.
|
skip(num)
|
obtain(num,num,LATER,ADVANCE,STATUS)
|
ADVANCE the stream past the next num elements
(or to stream termination, whichever comes first), and return a
vow for the stream's termination STATUS, to be resolved
LATER after these elements have been skipped.
|
readAllLater()
|
readLater(ALL,ALL)
|
Claim ALL the remaining elements of the stream, and
return a vow that will resolve to a list of these elements. Any
further input operations apply at the stream's termination.
|
becomesReady(num)
|
obtain(num,num,LATER,QUERY,STATUS)
|
A QUERY that returns a vow that resolves LATER,
when num elements have become ready (or all remaining elements
have become ready, whichever happens first). The returned vow then
resolves to the stream's expected termination status after these
elements have been consumed.
|
|
|