WriteStream’s (dirty?) little secret


Last week I was pair programming with a colleague and we had one of those jobs to do where you need to append some text to an existing string, but some stuff should only be appended under certain conditions. The usual way to do things like this is to create a WriteStream and write a few ifTrue’s and stuff write text onto the stream. Once the whole process is finished, you ask the WriteStream for its contents and done you are.

The code usually looks something like this:

str := WriteStream on: aString.
self moreToSay ifTrue: [str space; nextPutAll: self additionalString.].
^str contents

 

Most of the times I’ve seen and used this to stream on a freshly instantiated String. But this time, we had an existing one and the result surprised both of us. We both use Smalltalk for far more than ten years, but we both weren’t aware of this.

So what exactly happened?

We had a String that said ‘I am a nice String’, and if we assume the

additionalString

was

'hello'

, the result of the method was a String that said

' hello nice String'

. Wow. Never saw that. So we used a workspace and inspected

WriteStream on: 'Test'

. This creates a

WriteStream

whose position is 0, just like the readLimit. So putting something onto the Stream starts at position 1, not after the String you opened the Stream on. Funny.

There is, however, a class Method named

with:

that does exactly what we wanted. Inspecting

WriteStream with: 'Test'

yields a

WriteStream

with a position and redLimit of 4, so putting onto that Stream will append to the existing text.

I am sure every single Smalltalker out there knew already, right?
We didn’t, because most of the times you start with an empty string and you always get the desired behavior in these cases. BTW, we tried in VAST and Pharo, your mileage may differ…