Imagine a box of tissue: Each time you pull out a tissue, the next one becomes available. Stream objects represent a means of accessing every member of a collection one-by-one as if they were being pulled out of a box of tissue.
The interface to a
StreamObject has two methods. One for getting the value at the current position in the stream (called item() or value()) and another for getting a
StreamObject that represents the next position in the stream (called next() or tail()).
StreamObjects are
LazyObjects which means that they use
LazyEvaluation to calculate both value() and next(). This makes
StreamObjects able to do things that normal
ExternalIterators (and that is what they really are) cannot. For instance, a
StreamObject can represent an infinite collection such as a stream of random numbers or the set of all integers. Also, a stream is guaranteed to yield the same set of successors every time it is accessed, so the same stream can be saved and passed from client to client even if the collection it came from changes. Of course this is not always a desirable quality but the original collection can always generate a new stream that represents its current state.
LazyEvaluation also prevents the
StreamObject from doing expensive calculations (like accessing a database to obtain a value for the value() method) unless they are actually needed.
But an
ExternalIterator can use
LazyEvaluation, too. For example, the Stream class in Smalltalk is an
ExternalIterator (see subclass
ReadStream) but Random is a Stream that generates an infinite stream of random numbers. So they really aren't that different.
Functional streams yield the same set of successors every time they are accessed, but it seems to me that a stream OBJECT would not. So the name of this page doesn't seem quite right. --
The intent of a
StreamObject is that it would imitate a Functional stream exactly and thereby provide the same advantages. I believe that you are correct that a
StreamObject is an
ExternalIterator that uses
LazyEvaluation and has the same interface as a Functional stream. If a
StreamObject implemented
CallByNeed semantics (presumably through caching) then it would, indeed, return the same set of successors every time it was accessed. However, if the underlying collection were to change during the initial traversal, the
StreamObject would fail to reproduce the sequence that the collection held at the time the
StreamObject was created. --
PhilGoodwin
CategoryObjectFunctionalPatterns