Computing Streams
Here's an interesting tweak on existing Smalltalk ideas that Martin Kobetic and I wrote on the plane on the way back from our last planning meeting. Martin was wishing that streams were more composable, so that you could implement more complex encodings by just wrapping streams on top of each other. This is tricky to do with the existing streams and encodings. I was also talking about implementing some of the collection iteration methods on stream. VisualWorks already has do: implemented for readStreams, but things like collect: and select: can be very useful.
So Martin pointed out that those methods act on the stream like it was a collection, and you'd like an operation that acted on it like it was a stream. We started calling these "ing" methods - collecting:, selecting:, injecting:into:, and so forth. So,
#( 1 2 3 4 5) readStream selecting: [:each | each odd].
gives you back a readStream which, if asked repeatedly for its next element, will give you 1, then 3 then 5, then nil.
This is handy enough by itself, but gets more interesting. For one thing, you can stack these streams on top of each other. One of the standard ugly bits in Smalltalk is to do a collect: and select: together in order to filter out some elements, then apply a transformation to the other. You can just wrap one in the other, but then you end up creating an intermediate collection that you never use, and it's inefficient. Alternatively, you can implement a method that does them both at once, which I've seen called #collect:when:. But then you have to wonder how many of these special case combinations you'll end up writing. Using streams, you can wrap them, and no intermediate collection is required. So
(#( 1 2 3 4 5) readStream selecting: [:each | each odd]) collecting: [:each | each squared].
gives you back a readStream whose elements would be 1, 9 and 25, but without the overhead of creating the intermediate collection.
For starting out as a thought experiment, this turns out to be a very nice and natural metaphor. Not only that, but we we were able to implement it all during the plane ride. Most of the standard iteration methods map very simply to this, although we didn't come up with a sensible meaning for detect:. It's in a package called ComputingStreams, that's published in the public repository. Next post, we'll look at some of the hairier computations you can do with these.