On 10. 9. 2019 14:54, Richard O'Keefe wrote:
I think it's fair to say that #<< *is* a bug.
There does not seem to be any coherent description of what it means.
It's overloaded to mean *either* #nextPut: *or* #nextPutAll: *or*
something else, in some confusing ways.
CommandLineHandler           #nextPutAll: (sent somewhere else)
Integer                      left shift (someone has been smoking too much C++)
NonInteractiveTranscript     #show: = locked #print:
SocketStream                 #putOn: (which may itself act like
                              #nextPut:, #nextPutAll:, #print,
                              put elements sans separators, or
                              something else)
Stream                       #putOn: (see above)
WriteStream                  either #nextPutAll: or #putOn:
Transcript                   #show: = locked #print:
ThreadSafeTranscript         #show: = locked #print:
VTermOutputDriver            #putOn:
VTermOutputDriver2           #asString then #nextPutAll:
ZnEncodedWriteStream         #nextPutAll:
ZnHtmlOutputStream           #asString then #nextPutAll:
SequenceableCollection class #streamContents:

As was once said about PL/I, #<< fills a much-needed gap.
When I see #print:, or #nextPut:, or #nextPutAll:, I know
what to expect.  When I see #putOn:, I have in general no
idea what will happen.  And when I see << it is worse.

I don't think so. I have pretty coherent view of how << can work. In Amber this coherent view helped to create Silk library for DOM manipulation by treating a DOM element as a ind of a stream.

Having simple thing working (<< aCollection unpack the collection, having putOn: to be able to customize how object are put on stream) can help a lot; if, things are kept consistent.

One point of << is to imitate C++'s composition of outputs.
That might work, too, if only there were some agreement
about what #nextPutAll: returns.  There is not.  It might
return the receiver.  It might return some other stream
related to the receiver.  It might even return the collection
argument.  So when you see
  a << b << c
in general you not only do not have a clue what (a) is going
to do with (b) but you have no idea what object the message
<< c will be sent to.


This is strawman. We know what str << a << b << c does if we know what is output of #<<, it has nothing to do with #nextPutAll:. And it's simple, STream >> << should return self, and we're done.

Now let's see if we can puzzle out what
Array streamContents: [ :s | s << 10 << '10' << #(10 '10') ]
does.
The output will be going to a WriteStream.
aWriteStream << anInteger
is not, but is like, aWriteStream print: anInteger.
So we add $1 and $0.
aWriteStream << aString
reduces to aWriteStream nextPutAll: aString.
So we add $1 and $0.
aWriteStream on anArray << anotherArray
reduces to aWriteStream nextPutAll: anotherArray.
So we add 10 and '10'.
Thus the result we get is
#($1 $0 $1 $0 10 '10').
What result we should *expect* from this muddle I cannot say.

#(10 '10' 10 '10')

Of course.

After all, I put things on Array stream, which holds objects, not a character stream.

If, on the other hand, you wrote explicitly
Array streamContents: [:stream |
   stream print: 10; nextPutAll: '10'; nextPutAll: #(10 '10')]
you would have an easy time figuring out what to expect.

I see nextPut[All]: as low-level put API, and print:, write: and << as high-level one. I would not combine them.

I actually combine print: with write: to nice effect in Amber. Lot of code which actually export source to the disk uses combination of these two to enhance readability (IMO). For example:

exportTraitDefinitionOf: aClass on: aStream
        "Chunk format."

        aStream
                write: 'Trait named: '; printSymbol: aClass name; lf;
                tab; write: 'package: '; print: aClass category; write: '!!'; 
lf.
        aClass comment ifNotEmpty: [
                aStream
                write: '!!'; print: aClass; write: ' commentStamp!!'; lf;
                write: { self chunkEscape: aClass comment. '!!' }; lf ].
        aStream lf

As write: and << are synonyms in Amber (so they probably was in some part of Pharo history), I chose to pair print: keyword selector with write: keyword selector from the style point of view.

Also, since write: is <<, I can write: a collection of pieces to put and I don't need to cascade lots of write:s.

What I wanted to illustrate is, good implementation of << can be pretty useful.

By the way, there is no standard definition of #show:, but in
other Smalltalk systems it's usually a variant of #nextPutAll:,
not a variant of #print:.  There's no denying that locked output
is useful to have, but #show: is not perhaps the best name for it.

Herby


Reply via email to