As I type this, two devoted Cincom engineers are working thru a large number of changes accrued for the last two weeks, integrating and building a development build for VisualWorks 7.9.
And when they're done, an old friend, will have made a big step into retirement. The friend I speak of, is the very venerable ParagraphEditor object, around since the early Smalltalk-80 days.
Why retirement? Well, he was old and in the way.
For a long time, his replacement, TextEditorController has been doing most of the work. Most of the views in the system default to him, and often for those that don't, code exists to replace the default ParagraphEditor with a TextEditorController instance.
He's in the way, because we'd like to actually do some work in this area, but because the responsibilities bounce back and forth between the two, it's really difficult. Not just to implement new features, but then to figure out how they should work for the more limited ParagraphEditor, if at all. Archeology work here at Cincom, convinces me that TextEditorController always was meant as an eventual replacement of ParagraphEditor.
We're doing this a little different than when we folded the classes ApplicationWindow and ScheduledWindow together. In a current system, those two names refer to the same object. If you have method extensions or overrides, they end up in the same place. Because of the size and amount of extensions and overrides that get done to ParagraphEditor and TextEditorController, we've decided not to try and do anything clever like we did in the Window situation. The opportunity for confusion and image destruction just seemed to high.
So ParagraphEditor stays around as a distinct class for the time being. But TextEditorController no longer exists under it, but has been placed at the same level. All references to ParagraphEditor in the system have been changed to TextEditorController. All class side services that ParagraphEditor provided (things like copy/selection memory) have been moved to TextEditorController, and the ParagraphEditor methods exist still, to forward over to the TextEditorController ones.
At some point we'll empty out all of the instance behavior ParagraphEditor, and mark the class side methods as deprecated. And if I can find a nice API to help me, we might even make it so that when packages or parcels load that extend or override the former ParagraphEditor, you get a note that your methods aren't in the right place anymore.
So ParagraphEditor will stay around, but literally, he'll just be a shell of his former self.
So to ParagraphEditor we say Thank You. You've earned your place in history. Now enjoy your retirement.
Friday, April 29, 2011
Thursday, April 28, 2011
a := b + (b := a)
C programers, should they choose, can revel in some truly tricky bits of code. It's a fine line between simply elegant and truly evil. We don't get to have as much fun in Smalltalk, but sometimes...
So I have this chunk of code that interfaces with some Windows DLLs. And it has one of these great interfaces that goes like "call this function that takes more arguments than a large semi truck has wheels, and some of them are arrays, and some of them are how big the arrays are, and if your arrays aren't big enough for the task at hand, we'll respond with an particular error, and then you make your arrays bigger and try again."
Calling the code doesn't lend itself very well to a method extraction, since I have to set up a bunch of parameters via DLLCC pointers and such. So I end up with a largish method that looks like:
The thing is, I'm fascinated by papers and talks about memory managers and garbage collectors. Apparently, one of the growth vectors that ends up working really well at balancing too rapid growth, versus not fast enough, is the Fibonacci Series. I decided I'd like to increment my candidate size along the Fibonacci line.
With all the code I had in the method though, I didn't want to waste a bunch of code that was about incrementing to the next Fibonacci number. And that's where the post subject came into play. I was looking for a simple one liner to ratchet a Fibonacci number forward, and stumbled on it
I thought it was cool for a number of reasons. The symmetry itself appealed to me, both of the operands and variable names, and at the same time the way the parentheses played an asymmetric role.
This kinda "tricky" would be risky (I just rhymed) in C with an optimizer turned on. Luckily, Smalltalk is pretty good about guaranteeing its order of method evaluation. So what I'm really doing here is "stealing from the stack" to avoid having to have an intermediate variable. The old value of b is placed on the stack first, and then b is updated, but since the original value is on the stack already, I get the addition I want.
So I thought it was kinda elegant, sort of simple even, but definitely tricky. Probably too tricky, since people have to look at it to figure out what's doing. What do you think? Am I even close to that "fine line"?
So I have this chunk of code that interfaces with some Windows DLLs. And it has one of these great interfaces that goes like "call this function that takes more arguments than a large semi truck has wheels, and some of them are arrays, and some of them are how big the arrays are, and if your arrays aren't big enough for the task at hand, we'll respond with an particular error, and then you make your arrays bigger and try again."
Calling the code doesn't lend itself very well to a method extraction, since I have to set up a bunch of parameters via DLLCC pointers and such. So I end up with a largish method that looks like:
do a bunch of setup.
set a candidate size.
[increment the candidate size and allocate more stuff accordingly.
result := make the call.
result == needMoreMemory] whileTrue.
commit to the final sizes
clean more stuff up
The thing is, I'm fascinated by papers and talks about memory managers and garbage collectors. Apparently, one of the growth vectors that ends up working really well at balancing too rapid growth, versus not fast enough, is the Fibonacci Series. I decided I'd like to increment my candidate size along the Fibonacci line.
With all the code I had in the method though, I didn't want to waste a bunch of code that was about incrementing to the next Fibonacci number. And that's where the post subject came into play. I was looking for a simple one liner to ratchet a Fibonacci number forward, and stumbled on it
a := b + (b := a)
I thought it was cool for a number of reasons. The symmetry itself appealed to me, both of the operands and variable names, and at the same time the way the parentheses played an asymmetric role.
This kinda "tricky" would be risky (I just rhymed) in C with an optimizer turned on. Luckily, Smalltalk is pretty good about guaranteeing its order of method evaluation. So what I'm really doing here is "stealing from the stack" to avoid having to have an intermediate variable. The old value of b is placed on the stack first, and then b is updated, but since the original value is on the stack already, I get the addition I want.
So I thought it was kinda elegant, sort of simple even, but definitely tricky. Probably too tricky, since people have to look at it to figure out what's doing. What do you think? Am I even close to that "fine line"?
Wednesday, April 27, 2011
Burned By Bein' Lazy Again
Yep, it happened again. I spent a good 30 minutes plus, hunting down a bug that was caused by use of Lazy Initialization. One of those infinite recursion, open-lots-of-windows, tough-to-get-a-handle-on sorts.
Over the years, since I was first introduced to this "pattern", this happens once a year or so. It would happen more, if I used it more. This recent case was a case where I had broke my personal rule a while back, and it caught up with me. Again.
It's not a totally evil pattern, I see its value (and use it) for things like class var singletons.
The problem I have with it is one of Predictability and Responsibility. It seems like an encapsulated thing to do. You 'grow' the state of an object as different callers call upon for it services. So the state comes into being only as its needed. For simple patterns, this works fine:
and
No problems. It's hard to imagine what will go wrong at this point. What often happens though, is that objects evolve over time. Different people come along and maintain them. They do so, not with the whole object in mind, but just looking at one view. So someone discovers that foo and bar actually have some interplay. And we end up with
And then someone later does something like
The thing is, you might get away with this for a while. It's quite possible that when this was done, all uses of the object were using a setter to set foo before either accessor is invoked. So things Just Work(tm). Until later when someone changes the order of the way the object is being talked to.
In short, as the nature of lazy initializers grows in complexity, the odds rise that the object has hidden expectations about how it has to be interfaced with. And that is anything but encapsulated. Now you have the internal implementation of the object leaking out in hard to see or document ways.
Over the years, since I was first introduced to this "pattern", this happens once a year or so. It would happen more, if I used it more. This recent case was a case where I had broke my personal rule a while back, and it caught up with me. Again.
It's not a totally evil pattern, I see its value (and use it) for things like class var singletons.
The problem I have with it is one of Predictability and Responsibility. It seems like an encapsulated thing to do. You 'grow' the state of an object as different callers call upon for it services. So the state comes into being only as its needed. For simple patterns, this works fine:
foo
^foo ifNil: [foo := 42]
and
bar
^bar ifNil: [bar := 18]
No problems. It's hard to imagine what will go wrong at this point. What often happens though, is that objects evolve over time. Different people come along and maintain them. They do so, not with the whole object in mind, but just looking at one view. So someone discovers that foo and bar actually have some interplay. And we end up with
foo
foo ifNil: [self useConsistentFooBars ifTrue: [foo := self bar * 10]].
^foo
And then someone later does something like
bar
bar ifNil: [self useConsistentFooBars ifTrue: [bar := self foo / 10]].
^bar
The thing is, you might get away with this for a while. It's quite possible that when this was done, all uses of the object were using a setter to set foo before either accessor is invoked. So things Just Work(tm). Until later when someone changes the order of the way the object is being talked to.
In short, as the nature of lazy initializers grows in complexity, the odds rise that the object has hidden expectations about how it has to be interfaced with. And that is anything but encapsulated. Now you have the internal implementation of the object leaking out in hard to see or document ways.
Subscribe to:
Posts (Atom)