On Jun 8, 2009, at 9:32 PM, Nicolas Cellier wrote:
About this subject, here is a little reflexion explaining why Notification are not my preferred solution anymore (though I defended it at http://bugs.squeak.org/view.php?id=6755 ).
If you remember animated discussions about EndOfStream, then you might say Oh no, not again!
no if we can learn something in the process discussion is always worth.
However, my opinion has evolved closer to Andreas' one.
this
It still don't find == nil a good solution for generic Stream (though acceptable for character streams). Neither do I reject the idea of catching an EndOfStream : it can be efficient if the event is rare (in other words, if the stream is long, or if there are many wrappers, each testing == nil or atEnd).
What I find a big mistake now is to generalize use of an Exception with a default action. I know, this default action is for backward compatibility (answer nil). But let's take an example showing how it can be bad:
First load ConnectEndOfStream-M6755-nice.1.cs and RepeatUntil-M6755-nice.1.cs from http://bugs.squeak.org/view.php?id=6755 Then try this:
| str sum buffer char | str := '123 456 798' readStream. sum := 0. buffer := WriteStream on: (String new: 16).
[char := str next. char isSeparator ifTrue: [buffer isEmpty ifFalse: [sum := sum + buffer contents asNumber. buffer reset]] ifFalse: [buffer nextPut: char]] repeatUntil: EndOfStream. buffer isEmpty ifFalse: [sum := sum + buffer contents.]. ^sum
Yes, it just split a string on separators, convert every packet into a number and sum the numbers. The example is silly, don't comment on that. There are plenty other ways to split a String.
The problem is that the loop will stop after first number... What's going on? Well, asNumber uses a readStream to perform the conversion, reads past end, and signals an EndOfStream...
That is bad... I should have protected the Exception handling with:
| str sum buffer char | str := '123 456 798' readStream. sum := 0. buffer := WriteStream on: (String new: 16).
[[char := str next. char isSeparator ifTrue: [buffer isEmpty ifFalse: [sum := sum + buffer contents asNumber. buffer reset]] ifFalse: [buffer nextPut: char]] repeat] on: EndOfStream do: [:exc | exc receiver == str ifTrue: [exc return] ifFalse: [exc pass]]. buffer isEmpty ifFalse: [sum := sum + buffer contents.]. ^sum
Oh yes, easy. But imagine now that str is a wrapper on a FileStream... (for example an InflateStream).
Yeap it does not scale well in complexity for something that should be quite simple: there is not element anymore. :)
Exception handling soon becomes hard to write, because you might have to walk up the Context stack...
There are better examples where I want Exceptions, so I'd like the choice to be in programmers hands. - 1) Raising an EndOfStream Exception (and not a Notification as I suggested) should be optional - 2) The Exception should not have a default action.
The simple thing to do is to add an endOfStreamAction instanceVariable to Stream or ReadStream. If a read past end is attempted, then ^endOfStreamAction value.
nil value = nil, so letting this inst var uninitialized will lead to backward compatibility (at minimal cost concerning efficieny!). filling the inst var with a block comes to mind immediately, and among natural choice is: Stream>>willSignalEndOfStream self endOfStreamAction: [EndOfStream signal]
There might be more work for Stream wrappers, but that sounds a cheap and efficient idea to reconcile both worlds.
What will protect above code example from an internal low level EndOfStream? Nothing, but the fact that programmers using explicitely #willSignalEndOfStream will know what they do! Either they will protect with a handling block, or advertise enough that the code might generate spurious EndOfStream on malformed inputs...
sounds like a good compromise.
Of course, I must remind: use Exception handling pattern but don't abuse. Programming with these deserve great care!
Cheers
Nicolas
_______________________________________________ Pharo-project mailing list Pharo-project@lists.gforge.inria.fr http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project