Thanks for that fantastic explanation, Nicolas. If I were to try to distill your point, it's that each class in Squeak's legacy Stream hierarchy has its own view on how ITS concrete instances interpret the meaning of the API. And while, thankfully, they're mostly consistent with #next, #peek, and #nextPut:, they're not at all on other messages like #contents. The result of this is that the classes are mostly silo'ed from each other because it's complex to try to start sharing the behaviors. That was my take-away, anyway, please let me know if I missed it.
One thing I'm not sure about is whether you feel commingling read ability with write ability in the same object is a bad thing in an abstract sense? Or, it just didn't get implemented via a well-planned start in Squeak's case (organic growth), and so it's furthering dependencies on something that is hacked together and hard to change.
You mentioned the database exception, and sometimes it's hard for me to think outside of that context, so ReadWriteStream feels like a natural encapsulation of behaviors for accessing and updating file contents. If there's any inherent advantage to splitting the core streaming capabilities (reading, writing and positioning) into separate classes, or whether all those should be the API of all streams (but some may have to delete positioning via #shouldNotImplement). That would leave the class-hierarchy abstractions to focus on the data itself for things like conversion, compression, encryption, etc. instead of differing stream core capabilities...
Regards,
Chris
Notes 2007
In response to the issues raised below:
1.
The code in the book is wrong. It works for the ReadStream
class, but not the ReadWriteStream class.
2.
s := ReadWriteStream on: #(1 2).
s inspect.
The readLimit variable is 0
3.
Look at the ReadWriteStream contents method:
contents
"Answer with a copy of my collection from 1 to readLimit."
readLimit _ readLimit max: position.
^collection copyFrom: 1 to: readLimit
^ collection copyFrom: 1 to: 0 will be #().
[dae 15Dec07]
Are there bugs in the ReadWriteStream class in Squeak 3.5?
I've been reading the "Squeak: A Quick Trip to ObjectLand" book and have come across some problems with getting the examples using ReadWriteStream to work (on page 174-175). I don't get the same results as the book states as one should expect.
For instance, the following is a series of message sends from the book and what it says are the expected returned values:
s := ReadWriteStream on: #(1 2).
s next. "expected returned value should be 1"
s next. "...should be 1"
s atEnd. "...should be true"
s reset.
s next. "...should be 1"
s position. "...should be 0"
However, this is what I actually got:
s := ReadWriteStream on: #(1 2).
s next. ".result is nil while expected result should be 1 according to the book"
s atEnd. "results in true"
s position. "Answers 0"
s reset.
s next. "result is nil should be 1 according to the book"
s position. "Again, answers 0 while it should be 1"
s atEnd. "true again despite reset message send"
s contents. "#()"
I stepped through the on: message sent to ReadWriteStream and noticed that the read limit is set to 0.
I tried sending the with: message to see what happened.
s := ReadWriteStream with: #(1 2).
s next. "result returned is nil"
s position. "2"
s atEnd. "true"
Again, the next message returned nil instead of 1. Although, this time the position messaged returned 2.
The results are closer to what the book expects if a reset message is sent and the with: creation method is used instead of on:.
s := ReadWriteStream with: #(1 2).
s reset.
s next. "1 as expected"
s next. "2 likewise, as expected"
s atEnd. "true"
I've written an unit test for ReadWriteStream, ReadWriteStream, which exposes these failings. Is it possible if those responsible for ReadWriteStream could please verify the tests assumptions?