My coding is strongly influenced by Kent Beck and his coding patterns. I remember reading his column in The Smalltalk Report and seeing the evolving consensus of how to code Smalltalk. My iteration block still have :each or :each<something> as the binding variable. In his "Fundamentals of Smalltalk Programming Technique", Andres Valloud makes the point that code formatting is a pointless debate and that we should be comfortable reading any Smalltalk code. And he's right. But I'd still like to know what others find more readable. It's like learning to write gooder English.
For example, I trip over the extra white spaces inside blocks that most of the Seaside code has. Where I'd write a method like...
aBoolean
ifTrue: [self doThis]
ifFalse: [self doThat].
aList do: [:each | self doSomethingWith: each]
...I find the Seaside code is formatted as...
aBoolean
ifTrue: [ self doThis ]
ifFalse: [ self doThat ].
aList do: [ :each | self doSomethingWith: each ]
...I wonder why that convention was adopted. In my eyes the block is just not hugging the code enough. I don't find that style in other Smalltalk code. And it really does not matter; I'd just like to understand the though behind it.
Another example: In our VW application, the framework code uses abbreviations for local and parameter variables, so I see a lot of code like printOn: aStrm and | errMsg idx t v m s | ... drives me nuts. But it was adopted because of how easy it used to be to name a local variable the same as an instance variable. We do refactor code this kind of code now as we maintain it, but it's easier to accept when the thought behind it is understood.
And here's a pattern question: when is it better to double-dispatch? In our VW code was have an #echo method which writes a printString of the receiver to the Transcript, like the newish VW #out method (unfortunate name conflict with an Opentalk method). We also have #echo: which writes both the receiver and the argument to the Transcript, so...
'Time' echo: Time now
...shows in the Transcript as...
Time 10:10:54 AM
I added the same code to our VA application. So what's the best way to write this method? We don't want strings to be printed with single quotes, like...
Transcript cr; show: 'this string' printString
'this string'
vs.
...so we can implement #echo on Object as Transcript cr; show: self printString and on String as Transcript cr; show: self. But what about #echo: ? We could implement on Object as...
Object>>echo: anObject
Transcript cr; show: self printString, ': ', anObject printString
...(I prefer the : delimiter to just a space) and we'd need...
String>>echo: anObject
Transcript cr; show: self , ': ', anObject printString
...but what if anObject is a String? You would end up with superfluous single quotes. You could add a new method like #asEchoString with an Object and String implementation and send that instead of #printString, which is easy to read, or you could try double dispatching, which I decided to do (mostly out of curiosity). In this case I don't think it's a better pattern, but it's interesting (and very useful for more complex problems). And I like that they are all one line method.
Here is what my implementation (no value returned so that a := b echo answers b)...
Object>>echo
self printString echo
String>>echo
Transcript cr; show: self.
Object>>echo: anObject
anObject echoAfter: self printString
String>>echo: anObject
anObject echoAfter: self
Object>>echoAfter: aString
aString echoString: self printString
String>>echoAfter: aString
String>>echoString: aString
Transcript cr; show: self , ': ', aString
Transcript cr; show: 'this string' printString
'this string'
vs.
Transcript cr; show: 'this string'
this string...so we can implement #echo on Object as Transcript cr; show: self printString and on String as Transcript cr; show: self. But what about #echo: ? We could implement on Object as...
Object>>echo: anObject
Transcript cr; show: self printString, ': ', anObject printString
...(I prefer the : delimiter to just a space) and we'd need...
String>>echo: anObject
Transcript cr; show: self , ': ', anObject printString
...but what if anObject is a String? You would end up with superfluous single quotes. You could add a new method like #asEchoString with an Object and String implementation and send that instead of #printString, which is easy to read, or you could try double dispatching, which I decided to do (mostly out of curiosity). In this case I don't think it's a better pattern, but it's interesting (and very useful for more complex problems). And I like that they are all one line method.
Here is what my implementation (no value returned so that a := b echo answers b)...
Object>>echo
self printString echo
String>>echo
Transcript cr; show: self.
Object>>echo: anObject
anObject echoAfter: self printString
String>>echo: anObject
anObject echoAfter: self
Object>>echoAfter: aString
aString echoString: self printString
String>>echoAfter: aString
aString echoString: self
String>>echoString: aString
Transcript cr; show: self , ': ', aString
'test' echo: 123 >> 'test: 123''test' echo: 'this' >> 'test: this'123 echo: 'this' >> '123: this'123 echo: 456 >> '123: 456'
Bob, great post! I really like your echo. I'm tempted go change out to that. I really like, it works well. When I originally did out, it was lot like your echo. But then I added stdout, which I use more often than out now. One thing that I liked about those was that they kind of went together. You have out, and you have stdout. If I changed out to echo, then... you have echo and stdout.
ReplyDeleteThought 2. You had a number of different things in your post...
ReplyDeleteI, like you, really liked the evolution of standard style. Back in the day, in the early days of ObjectWorks, I remember how impressed I was by the consistency of the system. Things were formatted the same. Variable naming conventions were consistent. I too use 'each' and 'eachSomething' and get cranky when I see 'ea'. Vassili Bykov once left an offhand comment about some code he was looking at that was rich on abbreviations "just think of the greenhouse gas savings by being frugal with these few characters. The world is a better place, because this person obviously was recycling his characters for bigger and better uses."
I would agree that you should be able to read any sort of Smalltalk, or any sort of C, or any sort of whatever. But I don't think its right for authors to abuse that. Beck would later say "No topic generates more heat and less light than formatting...".
That said, I have no patience for team members who insist on being part of a team, but coding with their own style, taking advantage of the fact that "everyone should just suck it up and read my code the way I wrote it." The problems are numerous. You end up with a sort of mixed style as numerous stylists take part in the maintenance of the code. It's like reading a book where every page is written by a different author with a different style. It's readable, but it's distracting. Worse, people waste time, the time I'm paying them, to restyle code to their own tastes, as a constant burden rate on solving real problems. Frankly, I have better things to pay people to do, than "format code to their liking." Either automate it. Or come up with a standard that is easy to input manually and doesn't soak up time that could spent solving real problems. There's problems that need to be solved, solutions that need to be explored. If the greatest thing you have to offer, is a special way of indenting your blocks... you don't have much to offer.
Seaside uses this style because it looks better ;-)
ReplyDeleteWe once had very consistent code formatting in Seaside, then RB came along :-(