<div dir="ltr"><div><div><div><div><div><div><div><div>Hi Chris,<br>Yes, you are right, ReadWriteStream looks simple. But it is not.<br></div><div>It inherits from WriteStream which is a Stream that cannot read (self shouldNotImplement).<br>WriteStream inherits itself from PositionableStream which can neither read nor write (self subclassResponsibility).<br>PositionableStream is rather read-oriented though, see inst.var. readLimit and its usage which is clearly for delimiting read limit (thus the name).<br><br></div><div>WriteStream reuse 
readLimit 

with a slightly different purpose: indicate the right-most position written so far.<br>The purpose is to answer the whole contents even of position has been moved backward (rather than truncate to current position).<br></div>Hence, the natural invariant should be this one:<br>the readLimit should be incremented when writing pastEnd (past the right-most position written so far - readLimit).<br>But it is not... Indeed, primitive: 66 (nextPut:) increments position and ignores the readLimit, and just consider the writeLimit:<br>    rw := ReadWriteStream on: (String new: 10).<br>    rw nextPut: $a.<br>    rw instVarNamed: #readLimit<br>
PastEnd is interpreted as the physical limit - writeLimit - so as to have efficient primitives as long as there is room in the target collection.<br><br>That doesn't matter, because the position is shared for reading and writing operations.<br>Thanks to this property, we can to enforce the invariant differently:<br>hack every place where we might assign a position backward with a:<br></div></div>    readLimit := readLimit max: position.<br><br>The hack is both clever and fragile...<br></div><div>It's a nice hack, because as you observed, there are not so many methods requiring an overwrite...<br></div><div>But it's fragile, because it means 
 necessary

chirurgical operations in subclasses to maintain the invariant.<br></div><div>And if ever you want to subclass with a Stream maintaining two separate positions for read and write, boom!<br></div>




<div>Since this invariant isn't documented anywhere, probably because you just have to read the code :(<br></div><div>our best way to learn it will be pain: probably a feedback loop valued by the biological analogy ;)<br></div>
<div></div>

<br><div>And heavy chirurgy is what happens in the subclasses zoo which are further hacked...<br>The inst. var. position is no more the absolute position in underlying collection, but rather a relative position in some buffer/segment, both in case of StandardFileStream and CompressedSourceStream.<br></div><div>So these classes also modify the meaning of 
readLimit

inst. var. to be the number of bytes readable in the buffer.<br></div><div>And they need yet another way to access the absolute readLimit (endOfFile for CompressedSourceStream, and OS fseek-ftell-based for 
StandardFileStream).<br></div></div></div></div></div><div><div><div><div><div><div><br></div><div>Now, if you are about to modify one of these classes, and try and track usage of position/readLimit inst. var. you are in brain trouble.<br></div><div>Remember that position and self position might be different things...<br><br></div><div>So you are somehow right, ReadWriteStream is not at the level of awfullness I described, but it carries the seeds for this awfullness to be further developped.<br></div><div>Reusing inst. var. with a different intention across hierarchy is a good recipe for brain storm (a bug factory and a limitation to extensibility).<br></div><div>Most of the time, we don't need interleaved read/writes, except for a database backend or a few other stateful cases.<br></div><div>Instead, we mostly write then read. I already replaced a few instances of ReadWriteStream on this YAGNI principle, and would like this work to be continued, thus my simplistic reaction.<br></div><div>Of course, your case might differ.<br><br></div><div>Nicolas<br></div><div><div><div><div><div><div class="gmail_extra"><br><div class="gmail_quote">2018-05-02 0:16 GMT+02:00 Chris Muller <span dir="ltr"><<a href="mailto:asqueaker@gmail.com" target="_blank">asqueaker@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">C'mon, it has 9 tiny, concise methods, (15 including extensions from<br>
EToys and Compression) and so only Tim is allowed to characterize that<br>
as an "awful mess".  :)   No seriously, being the superclass for<br>
FileStream, it handles all of Squeak's file contents processing and works well.<br>
<br>
I guess Chris' explanation is a reasonable explanation for such glaring<br>
inconsistencies in behavior between superclass and subclass, so I<br>
ended up adding my own #content method which does what I want...<br>
<div class="gmail-m_-4768359240872069615gmail-HOEnZb"><div class="gmail-m_-4768359240872069615gmail-h5"><br>
<br>
<br>
On Tue, May 1, 2018 at 3:21 PM, Nicolas Cellier<br>
<<a href="mailto:nicolas.cellier.aka.nice@gmail.com" target="_blank">nicolas.cellier.aka.nice@gmai<wbr>l.com</a>> wrote:<br>
> IMO ReadWriteStream is an awfull mess and should better not be used at all.<br>
><br>
> 2018-05-01 22:00 GMT+02:00 Chris Muller <<a href="mailto:ma.chris.m@gmail.com" target="_blank">ma.chris.m@gmail.com</a>>:<br>
>><br>
>> Does anyone know why ReadWriteStream overrides #contents from WriteStream?<br>
>><br>
>> WriteStream behaves as I would expect<br>
>><br>
>>    |stream| stream := WriteStream on: String new.<br>
>>    stream nextPutAll: 'chris'; reset; nextPutAll: 'C'; contents     "---><br>
>> 'C'   as expected"<br>
>><br>
>> but ReadWriteStream doesn't...<br>
>><br>
>>    |stream| stream := ReadWriteStream on: String new.<br>
>>    stream nextPutAll: 'chris'; reset; nextPutAll: 'C'; contents     "---><br>
>> 'Chris'   unexpected!"<br>
>><br>
>> I want to reuse a ReadWriteStream, so I want #contents to honor the end<br>
>> position.  What's going on here?<br>
>><br>
>><br>
>><br>
><br>
><br>
><br>
><br>
<br>
</div></div></blockquote></div><br></div></div></div></div></div></div></div></div></div></div></div></div>