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! However, my opinion has evolved closer to Andreas' one. 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). 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... 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 [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
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 > [hidden email] > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project > _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
On Jun 8, 2009, at 9:32 PM, Nicolas Cellier wrote: > 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. To add emphasis: it can't have a default action or it is not doing its job. > 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... I hesitate to call the exceptions spurious; in situations where they would be described as such, I generally would use #nextAvailable: and write code that won't blow up. Exceptions do a very important job: they carry information from the point of detection of a problem to a place (often far removed on the stack) where a corrective action can be reasonably applied. To (attempt) to paraphrase your idea, you are proposing to remove the default action from EndOfStream, replacing that blunting of exceptions with an uninitialized endOfStreamAction instance variable that defalts to the same behavior, and can be set to ensure raising of exceptions. Fair? This is certainly worth a try. For my part, the cost would be I need to ensure that I set the exception behavior on every stream I create vs. ensuring I use a different set of methods. It's probably no worse, and maybe easier to enforce. I will generally hide ReadStream on: behind a helper, #readStream, so I could just as easily use #readStreamRobust to create the stream and activate exceptions. Sorry to press this so hard, but I cannot afford to have things breaking w/o telling me. At best, that wastes time to track down mysterious failures, and worse, it can be dangerous. It's not unlike the practice of building electronic enclosures with lots of exposed grounded metal. It took me a while to gain comfort with it, but the idea is that if a wire comes loose, it hits ground, pulls a breaker, and goes into a safe mode and gets attention as a side effect. Bill _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
Bill,
What I propose is to let programmer decide whether he will use Exception or not on endOfStream. And if he choose Exception, I underline that a defaultAction answering nil is not a panacea. But that defaultAction would still be an option if it solves your problem. Not sure my proposition does really solve the #next: problem you raised, indeed. The solution could be independant of endOfStream handling for this one. I support your request of creating a VW/Dolphin compatible #nextAvailable: and changing #next: to raise a NotEnoughElementAvailableError (maybe a subclass of EndOfStreamError). We would have a few package incompatibility, but, on ne fait pas d'omelette sans casser des oeufs... But I'm not convinced by the idea of duplicating the whole method protocol. Nor on duplicating the whole Stream class hierarchy, it's too big. That's were a classic delegation pattern can help (pluggable endOfStream). Of course, Trait composition is another option, and dynamic (on the fly) composition would be fun experimenting (as opposed to Static class definition). Keeping Traits as independant code units as possible is an interesting challenge (to explore the limits of such design). Nicolas 2009/6/9 Schwab,Wilhelm K <[hidden email]>: > > On Jun 8, 2009, at 9:32 PM, Nicolas Cellier wrote: > >> 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. > > To add emphasis: it can't have a default action or it is not doing its job. > > >> 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... > > I hesitate to call the exceptions spurious; in situations where they would be described as such, I generally would use #nextAvailable: and write code that won't blow up. Exceptions do a very important job: they carry information from the point of detection of a problem to a place (often far removed on the stack) where a corrective action can be reasonably applied. > > To (attempt) to paraphrase your idea, you are proposing to remove the default action from EndOfStream, replacing that blunting of exceptions with an uninitialized endOfStreamAction instance variable that defalts to the same behavior, and can be set to ensure raising of exceptions. Fair? > > This is certainly worth a try. For my part, the cost would be I need to ensure that I set the exception behavior on every stream I create vs. ensuring I use a different set of methods. It's probably no worse, and maybe easier to enforce. I will generally hide ReadStream on: behind a helper, #readStream, so I could just as easily use #readStreamRobust to create the stream and activate exceptions. Sorry to press this so hard, but I cannot afford to have things breaking w/o telling me. At best, that wastes time to track down mysterious failures, and worse, it can be dangerous. It's not unlike the practice of building electronic enclosures with lots of exposed grounded metal. It took me a while to gain comfort with it, but the idea is that if a wire comes loose, it hits ground, pulls a breaker, and goes into a safe mode and gets attention as a side effect. > > Bill > > > > _______________________________________________ > Pharo-project mailing list > [hidden email] > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project > _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
Nicolas,
Adding #nextAvailable: hurts nothing, but changing #next: (and #next too) will break existing code. In VW, it is only #next that has the silent failure trap, and while Cincom was willing to consider a change, they ultimately declined to make it. I am convinced that Dolphin gets it right, but I would be surprised to see us adopt its behavior. I would be thrilled if we did so, but doubt it will happen. Failing such breaking changes, I need to create a way to have my code work, which is why I started adding my own methods. Your pluggable exception/defaultAction idea is worth a shot. Some simple testing should show whether it can preserve current behavior and throw exceptions, respectively. I think I heard Stef as much as say he would support it if it does serve both masters. Stef? Bill -----Original Message----- From: [hidden email] [mailto:[hidden email]] On Behalf Of Nicolas Cellier Sent: Tuesday, June 09, 2009 2:34 AM To: [hidden email] Subject: Re: [Pharo-project] About EndOfStream notification [was Streams - #nextLine] Bill, What I propose is to let programmer decide whether he will use Exception or not on endOfStream. And if he choose Exception, I underline that a defaultAction answering nil is not a panacea. But that defaultAction would still be an option if it solves your problem. Not sure my proposition does really solve the #next: problem you raised, indeed. The solution could be independant of endOfStream handling for this one. I support your request of creating a VW/Dolphin compatible #nextAvailable: and changing #next: to raise a NotEnoughElementAvailableError (maybe a subclass of EndOfStreamError). We would have a few package incompatibility, but, on ne fait pas d'omelette sans casser des oeufs... But I'm not convinced by the idea of duplicating the whole method protocol. Nor on duplicating the whole Stream class hierarchy, it's too big. That's were a classic delegation pattern can help (pluggable endOfStream). Of course, Trait composition is another option, and dynamic (on the fly) composition would be fun experimenting (as opposed to Static class definition). Keeping Traits as independant code units as possible is an interesting challenge (to explore the limits of such design). Nicolas 2009/6/9 Schwab,Wilhelm K <[hidden email]>: > > On Jun 8, 2009, at 9:32 PM, Nicolas Cellier wrote: > >> 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. > > To add emphasis: it can't have a default action or it is not doing its job. > > >> 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... > > I hesitate to call the exceptions spurious; in situations where they would be described as such, I generally would use #nextAvailable: and write code that won't blow up. Exceptions do a very important job: they carry information from the point of detection of a problem to a place (often far removed on the stack) where a corrective action can be reasonably applied. > > To (attempt) to paraphrase your idea, you are proposing to remove the default action from EndOfStream, replacing that blunting of exceptions with an uninitialized endOfStreamAction instance variable that defalts to the same behavior, and can be set to ensure raising of exceptions. Fair? > > This is certainly worth a try. For my part, the cost would be I need to ensure that I set the exception behavior on every stream I create vs. ensuring I use a different set of methods. It's probably no worse, and maybe easier to enforce. I will generally hide ReadStream on: behind a helper, #readStream, so I could just as easily use #readStreamRobust to create the stream and activate exceptions. Sorry to press this so hard, but I cannot afford to have things breaking w/o telling me. At best, that wastes time to track down mysterious failures, and worse, it can be dangerous. It's not unlike the practice of building electronic enclosures with lots of exposed grounded metal. It took me a while to gain comfort with it, but the idea is that if a wire comes loose, it hits ground, pulls a breaker, and goes into a safe mode and gets attention as a side effect. > > Bill > > > > _______________________________________________ > Pharo-project mailing list > [hidden email] > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project > _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
2009/6/9 Schwab,Wilhelm K <[hidden email]>:
> Nicolas, > > Adding #nextAvailable: hurts nothing, but changing #next: (and #next too) will break existing code. In VW, it is only #next that has the silent failure trap, and while Cincom was willing to consider a change, they ultimately declined to make it. I am convinced that Dolphin gets it right, but I would be surprised to see us adopt its behavior. I would be thrilled if we did so, but doubt it will happen. Failing such breaking changes, I need to create a way to have my code work, which is why I started adding my own methods. > That's the price to pay, we have to break some #next: senders... Inside the image, we can fix it easily. Bt how many should we fix? I have 18 implementors (16 stream-like, 2 linked-list like) and 100 senders in core, but when looking at them, few are prepared to get a shorter collection than requested. For example, I read: (self next: 1) at: 1 or: header := strm next: 4. self nSamples: header first nLevels: header second. self setAlpha: header third beta: header fourth. IMO, the change you request is a must in a majority of cases... It's of course better to see an immediate EndOfStreamError rather than a delayed OutOfBoundError. For external packages, either the change is backported to other Squeak branches on a collaborative basis (preferred), or we'll have to maintain Pharo specific code on a competitive basis. Other squeak branches don't need to adopt our #next: behaviour, they just have to provide a #nextAvailable: hook for compatibility, so I don't think this is a real problem, especially if it enhances portability. For #next, I can understand VW declined: 1) they would have to change a bunch of senders 2) they have to guarantee a certain degree of portability to their clients, especially on such a heavily used kernel method. In Squeak code too, there are just too many senders of #next, the transition would be painfull without some backward compatibility. > Your pluggable exception/defaultAction idea is worth a shot. Some simple testing should show whether it can preserve current behavior and throw exceptions, respectively. I think I heard Stef as much as say he would support it if it does serve both masters. Stef? > > Bill > > Cost is small. Think of adding a Nile compatible hook too. > > -----Original Message----- > From: [hidden email] [mailto:[hidden email]] On Behalf Of Nicolas Cellier > Sent: Tuesday, June 09, 2009 2:34 AM > To: [hidden email] > Subject: Re: [Pharo-project] About EndOfStream notification [was Streams - #nextLine] > > Bill, > What I propose is to let programmer decide whether he will use Exception or not on endOfStream. > And if he choose Exception, I underline that a defaultAction answering nil is not a panacea. > But that defaultAction would still be an option if it solves your problem. > > Not sure my proposition does really solve the #next: problem you raised, indeed. > The solution could be independant of endOfStream handling for this one. > I support your request of creating a VW/Dolphin compatible > #nextAvailable: and changing #next: to raise a NotEnoughElementAvailableError (maybe a subclass of EndOfStreamError). > We would have a few package incompatibility, but, on ne fait pas d'omelette sans casser des oeufs... > > But I'm not convinced by the idea of duplicating the whole method protocol. > Nor on duplicating the whole Stream class hierarchy, it's too big. > That's were a classic delegation pattern can help (pluggable endOfStream). > Of course, Trait composition is another option, and dynamic (on the > fly) composition would be fun experimenting (as opposed to Static class definition). Keeping Traits as independant code units as possible is an interesting challenge (to explore the limits of such design). > > Nicolas > > 2009/6/9 Schwab,Wilhelm K <[hidden email]>: >> >> On Jun 8, 2009, at 9:32 PM, Nicolas Cellier wrote: >> >>> 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. >> >> To add emphasis: it can't have a default action or it is not doing its job. >> >> >>> 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... >> >> I hesitate to call the exceptions spurious; in situations where they would be described as such, I generally would use #nextAvailable: and write code that won't blow up. Exceptions do a very important job: they carry information from the point of detection of a problem to a place (often far removed on the stack) where a corrective action can be reasonably applied. >> >> To (attempt) to paraphrase your idea, you are proposing to remove the default action from EndOfStream, replacing that blunting of exceptions with an uninitialized endOfStreamAction instance variable that defalts to the same behavior, and can be set to ensure raising of exceptions. Fair? >> >> This is certainly worth a try. For my part, the cost would be I need to ensure that I set the exception behavior on every stream I create vs. ensuring I use a different set of methods. It's probably no worse, and maybe easier to enforce. I will generally hide ReadStream on: behind a helper, #readStream, so I could just as easily use #readStreamRobust to create the stream and activate exceptions. Sorry to press this so hard, but I cannot afford to have things breaking w/o telling me. At best, that wastes time to track down mysterious failures, and worse, it can be dangerous. It's not unlike the practice of building electronic enclosures with lots of exposed grounded metal. It took me a while to gain comfort with it, but the idea is that if a wire comes loose, it hits ground, pulls a breaker, and goes into a safe mode and gets attention as a side effect. >> >> Bill >> >> >> >> _______________________________________________ >> Pharo-project mailing list >> [hidden email] >> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project >> > > _______________________________________________ > Pharo-project mailing list > [hidden email] > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project > > _______________________________________________ > Pharo-project mailing list > [hidden email] > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project > _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
Nicolas,
You have some interesting observations re senders of #next: - fixing it is sounding like a good idea. Since you are not ready to take the plunge on #next, I will be forced to deprecate it for my own use and continue to use #nextOne, unless we take the pluggable exception route?? Stef, how should we proceed? I think this ultimately has to go to the board for a decision. For my part, I am happy to help, will gladly take advantage of whatever we fix, and will deprecate/add protocol to ensure correct behavior where we do not act. To summarize, the options appear to be: (1) add pluggable exceptions covering #next, #next:, etc.; any attempt to read off the end of a stream would invoke the set behavior, either an exception with teeth (no default action), or truncate/answer nil as appropriate. (2) Fix #next: on the grounds Nicolas has identified, specifically that many senders assume non-truncating behavior; #next would likely remain as-is. (3) Adopt Dolphin's behavior - I do not see us taking this branch, but list it for completeness. Either way, add #nextAvailable: - it is harmless to backward compatibility, so can go in in either case. Bill -----Original Message----- From: [hidden email] [mailto:[hidden email]] On Behalf Of Nicolas Cellier Sent: Tuesday, June 09, 2009 7:38 AM To: [hidden email] Subject: Re: [Pharo-project] About EndOfStream notification [was Streams - #nextLine] 2009/6/9 Schwab,Wilhelm K <[hidden email]>: > Nicolas, > > Adding #nextAvailable: hurts nothing, but changing #next: (and #next too) will break existing code. In VW, it is only #next that has the silent failure trap, and while Cincom was willing to consider a change, they ultimately declined to make it. I am convinced that Dolphin gets it right, but I would be surprised to see us adopt its behavior. I would be thrilled if we did so, but doubt it will happen. Failing such breaking changes, I need to create a way to have my code work, which is why I started adding my own methods. > That's the price to pay, we have to break some #next: senders... Inside the image, we can fix it easily. Bt how many should we fix? I have 18 implementors (16 stream-like, 2 linked-list like) and 100 senders in core, but when looking at them, few are prepared to get a shorter collection than requested. For example, I read: (self next: 1) at: 1 or: header := strm next: 4. self nSamples: header first nLevels: header second. self setAlpha: header third beta: header fourth. IMO, the change you request is a must in a majority of cases... It's of course better to see an immediate EndOfStreamError rather than a delayed OutOfBoundError. For external packages, either the change is backported to other Squeak branches on a collaborative basis (preferred), or we'll have to maintain Pharo specific code on a competitive basis. Other squeak branches don't need to adopt our #next: behaviour, they just have to provide a #nextAvailable: hook for compatibility, so I don't think this is a real problem, especially if it enhances portability. For #next, I can understand VW declined: 1) they would have to change a bunch of senders 2) they have to guarantee a certain degree of portability to their clients, especially on such a heavily used kernel method. In Squeak code too, there are just too many senders of #next, the transition would be painfull without some backward compatibility. > Your pluggable exception/defaultAction idea is worth a shot. Some simple testing should show whether it can preserve current behavior and throw exceptions, respectively. I think I heard Stef as much as say he would support it if it does serve both masters. Stef? > > Bill > > Cost is small. Think of adding a Nile compatible hook too. > > -----Original Message----- > From: [hidden email] > [mailto:[hidden email]] On Behalf Of > Nicolas Cellier > Sent: Tuesday, June 09, 2009 2:34 AM > To: [hidden email] > Subject: Re: [Pharo-project] About EndOfStream notification [was > Streams - #nextLine] > > Bill, > What I propose is to let programmer decide whether he will use Exception or not on endOfStream. > And if he choose Exception, I underline that a defaultAction answering nil is not a panacea. > But that defaultAction would still be an option if it solves your problem. > > Not sure my proposition does really solve the #next: problem you raised, indeed. > The solution could be independant of endOfStream handling for this one. > I support your request of creating a VW/Dolphin compatible > #nextAvailable: and changing #next: to raise a NotEnoughElementAvailableError (maybe a subclass of EndOfStreamError). > We would have a few package incompatibility, but, on ne fait pas d'omelette sans casser des oeufs... > > But I'm not convinced by the idea of duplicating the whole method protocol. > Nor on duplicating the whole Stream class hierarchy, it's too big. > That's were a classic delegation pattern can help (pluggable endOfStream). > Of course, Trait composition is another option, and dynamic (on the > fly) composition would be fun experimenting (as opposed to Static class definition). Keeping Traits as independant code units as possible is an interesting challenge (to explore the limits of such design). > > Nicolas > > 2009/6/9 Schwab,Wilhelm K <[hidden email]>: >> >> On Jun 8, 2009, at 9:32 PM, Nicolas Cellier wrote: >> >>> 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. >> >> To add emphasis: it can't have a default action or it is not doing its job. >> >> >>> 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... >> >> I hesitate to call the exceptions spurious; in situations where they would be described as such, I generally would use #nextAvailable: and write code that won't blow up. Exceptions do a very important job: they carry information from the point of detection of a problem to a place (often far removed on the stack) where a corrective action can be reasonably applied. >> >> To (attempt) to paraphrase your idea, you are proposing to remove the default action from EndOfStream, replacing that blunting of exceptions with an uninitialized endOfStreamAction instance variable that defalts to the same behavior, and can be set to ensure raising of exceptions. Fair? >> >> This is certainly worth a try. For my part, the cost would be I need to ensure that I set the exception behavior on every stream I create vs. ensuring I use a different set of methods. It's probably no worse, and maybe easier to enforce. I will generally hide ReadStream on: behind a helper, #readStream, so I could just as easily use #readStreamRobust to create the stream and activate exceptions. Sorry to press this so hard, but I cannot afford to have things breaking w/o telling me. At best, that wastes time to track down mysterious failures, and worse, it can be dangerous. It's not unlike the practice of building electronic enclosures with lots of exposed grounded metal. It took me a while to gain comfort with it, but the idea is that if a wire comes loose, it hits ground, pulls a breaker, and goes into a safe mode and gets attention as a side effect. >> >> Bill >> >> >> >> _______________________________________________ >> Pharo-project mailing list >> [hidden email] >> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project >> > > _______________________________________________ > Pharo-project mailing list > [hidden email] > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project > > _______________________________________________ > Pharo-project mailing list > [hidden email] > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project > _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
In reply to this post by Nicolas Cellier
On Jun 9, 2009, at 2:37 PM, Nicolas Cellier wrote: > 2009/6/9 Schwab,Wilhelm K <[hidden email]>: >> Nicolas, >> >> Adding #nextAvailable: hurts nothing, but changing #next: (and >> #next too) will break existing code. In VW, it is only #next that >> has the silent failure trap, and while Cincom was willing to >> consider a change, they ultimately declined to make it. I am >> convinced that Dolphin gets it right, but I would be surprised to >> see us adopt its behavior. I would be thrilled if we did so, but >> doubt it will happen. Failing such breaking changes, I need to >> create a way to have my code work, which is why I started adding my >> own methods. >> > > That's the price to pay, we have to break some #next: senders... > Inside the image, we can fix it easily. Bt how many should we fix? > I have 18 implementors (16 stream-like, 2 linked-list like) and 100 > senders in core, but when looking at them, few are prepared to get a > shorter collection than requested. > For example, I read: > (self next: 1) at: 1 > or: > header := strm next: 4. > self nSamples: header first nLevels: header second. > self setAlpha: header third beta: header fourth. > IMO, the change you request is a must in a majority of cases... so this looks like a good news :) > > It's of course better to see an immediate EndOfStreamError rather than > a delayed OutOfBoundError. > > For external packages, either the change is backported to other Squeak > branches on a collaborative basis (preferred), or we'll have to > maintain Pharo specific code on a competitive basis. > > Other squeak branches don't need to adopt our #next: behaviour, they > just have to provide a #nextAvailable: hook for compatibility, so I > don't think this is a real problem, especially if it enhances > portability. > > For #next, I can understand VW declined: > 1) they would have to change a bunch of senders > 2) they have to guarantee a certain degree of portability to their > clients, especially on such a heavily used kernel method. > In Squeak code too, there are just too many senders of #next, the > transition would be painfull without some backward compatibility. does a backward compatible with two path solution exist? > > >> Your pluggable exception/defaultAction idea is worth a shot. Some >> simple testing should show whether it can preserve current behavior >> and throw exceptions, respectively. I think I heard Stef as much >> as say he would support it if it does serve both masters. Stef? >> >> Bill >> >> > > Cost is small. > Think of adding a Nile compatible hook too. > >> >> -----Original Message----- >> From: [hidden email] [mailto:[hidden email] >> ] On Behalf Of Nicolas Cellier >> Sent: Tuesday, June 09, 2009 2:34 AM >> To: [hidden email] >> Subject: Re: [Pharo-project] About EndOfStream notification [was >> Streams - #nextLine] >> >> Bill, >> What I propose is to let programmer decide whether he will use >> Exception or not on endOfStream. >> And if he choose Exception, I underline that a defaultAction >> answering nil is not a panacea. >> But that defaultAction would still be an option if it solves your >> problem. >> >> Not sure my proposition does really solve the #next: problem you >> raised, indeed. >> The solution could be independant of endOfStream handling for this >> one. >> I support your request of creating a VW/Dolphin compatible >> #nextAvailable: and changing #next: to raise a >> NotEnoughElementAvailableError (maybe a subclass of >> EndOfStreamError). >> We would have a few package incompatibility, but, on ne fait pas >> d'omelette sans casser des oeufs... >> >> But I'm not convinced by the idea of duplicating the whole method >> protocol. >> Nor on duplicating the whole Stream class hierarchy, it's too big. >> That's were a classic delegation pattern can help (pluggable >> endOfStream). >> Of course, Trait composition is another option, and dynamic (on the >> fly) composition would be fun experimenting (as opposed to Static >> class definition). Keeping Traits as independant code units as >> possible is an interesting challenge (to explore the limits of such >> design). >> >> Nicolas >> >> 2009/6/9 Schwab,Wilhelm K <[hidden email]>: >>> >>> On Jun 8, 2009, at 9:32 PM, Nicolas Cellier wrote: >>> >>>> 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. >>> >>> To add emphasis: it can't have a default action or it is not doing >>> its job. >>> >>> >>>> 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... >>> >>> I hesitate to call the exceptions spurious; in situations where >>> they would be described as such, I generally would use >>> #nextAvailable: and write code that won't blow up. Exceptions do >>> a very important job: they carry information from the point of >>> detection of a problem to a place (often far removed on the stack) >>> where a corrective action can be reasonably applied. >>> >>> To (attempt) to paraphrase your idea, you are proposing to remove >>> the default action from EndOfStream, replacing that blunting of >>> exceptions with an uninitialized endOfStreamAction instance >>> variable that defalts to the same behavior, and can be set to >>> ensure raising of exceptions. Fair? >>> >>> This is certainly worth a try. For my part, the cost would be I >>> need to ensure that I set the exception behavior on every stream I >>> create vs. ensuring I use a different set of methods. It's >>> probably no worse, and maybe easier to enforce. I will generally >>> hide ReadStream on: behind a helper, #readStream, so I could just >>> as easily use #readStreamRobust to create the stream and activate >>> exceptions. Sorry to press this so hard, but I cannot afford to >>> have things breaking w/o telling me. At best, that wastes time to >>> track down mysterious failures, and worse, it can be dangerous. >>> It's not unlike the practice of building electronic enclosures >>> with lots of exposed grounded metal. It took me a while to gain >>> comfort with it, but the idea is that if a wire comes loose, it >>> hits ground, pulls a breaker, and goes into a safe mode and gets >>> attention as a side effect. >>> >>> Bill >>> >>> >>> >>> _______________________________________________ >>> Pharo-project mailing list >>> [hidden email] >>> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project >>> >> >> _______________________________________________ >> Pharo-project mailing list >> [hidden email] >> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project >> >> _______________________________________________ >> Pharo-project mailing list >> [hidden email] >> http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project >> > > _______________________________________________ > Pharo-project mailing list > [hidden email] > http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project > _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
In reply to this post by Schwab,Wilhelm K
> Nicolas,
> > Adding #nextAvailable: hurts nothing, but changing #next: (and #next > too) will break existing code. In VW, it is only #next that has the > silent failure trap, and while Cincom was willing to consider a > change, they ultimately declined to make it. I am convinced that > Dolphin gets it right, but I would be surprised to see us adopt its > behavior. I would be thrilled if we did so, but doubt it will > happen. Failing such breaking changes, I need to create a way to > have my code work, which is why I started adding my own methods. > > Your pluggable exception/defaultAction idea is worth a shot. Some > simple testing should show whether it can preserve current behavior > and throw exceptions, respectively. I think I heard Stef as much as > say he would support it if it does serve both masters. Stef? As I said I would like to have a one page description of the problem, solution, pros and cons May be put it on the wiki Right now we are messy in terms of process for changes. I personnally always hated the VW behavior because we lost a lot of time with code trapping error. But I do not want to take decision on my emotion because I could be wrong. So do a one page proposal with problem, solution, pros and cons. Stef _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
Stef,
A statement on pros/cons sounds like a good place to start. Re the VW/Dolphin behavior, the way to look at the errors is that they tell you what went wrong and where. If the errors truly are superfluous, then you probably should be using #nextAvailable:, which truncates and does not raise errors. You asked about a backwardly compatible solution. I think the pluggable exception idea is probably the best bet for that. Bill -----Original Message----- From: [hidden email] [mailto:[hidden email]] On Behalf Of Stéphane Ducasse Sent: Tuesday, June 09, 2009 3:20 PM To: [hidden email] Subject: Re: [Pharo-project] About EndOfStream notification [was Streams - #nextLine] > Nicolas, > > Adding #nextAvailable: hurts nothing, but changing #next: (and #next > too) will break existing code. In VW, it is only #next that has the > silent failure trap, and while Cincom was willing to consider a > change, they ultimately declined to make it. I am convinced that > Dolphin gets it right, but I would be surprised to see us adopt its > behavior. I would be thrilled if we did so, but doubt it will happen. > Failing such breaking changes, I need to create a way to have my code > work, which is why I started adding my own methods. > > Your pluggable exception/defaultAction idea is worth a shot. Some > simple testing should show whether it can preserve current behavior > and throw exceptions, respectively. I think I heard Stef as much as > say he would support it if it does serve both masters. Stef? As I said I would like to have a one page description of the problem, solution, pros and cons May be put it on the wiki Right now we are messy in terms of process for changes. I personnally always hated the VW behavior because we lost a lot of time with code trapping error. But I do not want to take decision on my emotion because I could be wrong. So do a one page proposal with problem, solution, pros and cons. Stef _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project _______________________________________________ Pharo-project mailing list [hidden email] http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-project |
Free forum by Nabble | Edit this page |