A Word From the Wise
Bill Wake wrote to me, regarding the two Smalltalk bowling articles. Among other remarks, he said:
A couple places in the article made me go "hmm..."
One was where you lazy-inited the original collection; is that a typical idiom for all collections? (It's normally something I'd worry about in C#/Java only if I had a lot of objects holding collections.)
The other is a concern in the leap to handling strikes. It seemed sort of like you just had all the pieces lying on the floor while you got it right. If I multiply by 3 to the size in Java, it's a noticeable amount of stuff.
Actually, the reason I did the lazy init was that otherwise I would have had to override the class method #new, and provide an #initialize method, and I didn’t want to do that. Not that it’s hard, I just didn’t want to. I don’t have any particular rule for when I’d do a lazy init and when I wouldn’t. I can think of at least these ways of looking at it:
- Lazy init things that will take up time or space to build, and that are only used in some circumstances. This will avoid extra storage and computation. OK, that sounds like good advice, but it also sounds like premature optimization.
- Do all initializations in an #initialize method. That's what it's for. That sounds good, too, but in a complex object I always forget to init something.
- Do all inits lazily, for consistency. Well, that makes me use accessors all the time, which I really don't like, and each accessor is a bit more confusing to look at, and it decentralizes knowledge of what the contents of the object are.
None of these is very compelling. I’m not sure what forces I’m responding to in which way I go, and I’m out of practice enough in Smalltalk that I’m likely a bit random anyway. Time permitting, I’ll look in Mr Beck’s Smalltalk Best Practice Patterns book, which is quite good.
Bill also mentions “the leap” to handling strikes. I think he means that step where I just wrote self strike ifTrue: and so on. That’s certainly a bigger bite than I typically would take in C#, no doubt about it. Partly I did it because I had done the bowling thing a day before and found that writing it up in the past tense wasn’t working, so I did it over. That meant I was probably more “in practice”. But mostly I did it because Smalltalk stays out of the way so well, and reminds you so neatly when you make a mistake, that typing in something like that is safer than it would be in other languages. It’s only about eight lines of code for the whole thing, after all!
But if I keep doing this Smalltalk series for a while, let’s watch to see whether I take different sized steps than I would in C#.
And if you’re reading and enjoying these articles, please drop me a note and let me know, since I’m concerned that there aren’t that many people interested in what programming looks like in this language. If you’re not reading them, or not enjoying them, please keep it to yourself.
Looking back over the few hours I spent doing the previous articles, a number of things come out to me.
One thing that comes out is how a simple typo can result in so much furor on the net. Some of you guys – you know who I’m talking about – have dirty minds!
Another thing that I didn’t measure but sure can feel is how the percentage of time spent programming versus writing the article changes in Smalltalk. In C# it felt like I programmed about half the time and wrote about half the time. In Smalltalk it feels lik ten percent programming and ninety percent writing. The programming just goes so much faster!
I met with Chet today at Borders in Brighton. I found myself going on about how great it was to use Smalltalk again, how smoothly it went even though I don’t understand all the nuances about name spaces and such that are in there, and how if I could figure out a way to make a living writing Smalltalk and writing about it, I would. We’re considering an application that a colleague needs, and remarked that right now we know a bit more about how to do it in C# and could get going sooner, but that it would clearly be more fun and more productive longer term, in Smalltalk.
It’s hard to be really optimistic about the future of Smalltalk, with the dark forces of Java and C# out there pretty much in the ascendency, but it will probably be around for a long time, a secret weapon in the kits of the people who know it.
One of the things that makes Smalltalk easier to develop in is that you don’t have to do all that declaration of types, and the compiler doesn’t keep checking up on you. Now when I first started with Smalltalk, I had been using a very strictly typed language, and I was expecting lots of defects due to using the wrong type. That didn’t happen. The most common defect by far is that you send a message to nil. That usually means you haven’t initialized the variable at all. Some compilers will notice that. But Smalltalk systems now have features like VisualWorks’s “Code Critic” that find that kind of thing and more. Putting the wrong type into a variable just doesn’t happen very often at all. I’m not sure why; it just doesn’t.
So since the defects don’t increase without the type declarations, you save typing and gain in flexibility. I’ve got an idea for how to demonstrate that later on in this article.
Anyway, for me the experiment so far is very interesting. Even feeling pretty rusty, I can already feel how much more productive I can be in this language than in C# or Java. It takes a while for a beginner to get there, but it’s well worth it if you get the chance.
Everyone's a Critic
One thing that I did when you weren’t looking was to add a creation method to the bowling game, and an intialize method. This let me remove the lazy init, and let me reference the rolls variable directly instead of using an accessor. I prefer direct access to instance variables when there aren’t any outside users. The affected code looks like this:
BowlingGame class>>new ^super new initialize initialize rolls := OrderedCollection new. roll: anInteger rolls add: anInteger
I wasn’t as consistent as I should have been in using the accessor. The only place I used it was in the #roll: method, to make sure rolls got initialized. Elsewhere I accessed rolls directly. That was a code smell and part of what Bill might have been talking about. Anyway, all better now.
I mentioned Code Critic. Let’s run that on BowlingGame and see what it says:
You’ll notice that you can click the browse button on the Code Critic, and if you do, it will give you a browser containing just the methods it criticized. This is one of the interesting aspects of Smalltalk: there are all kinds of ways to get a browser open on just the methods you care about, like the methods that access some variable.
Here’s one of the methods the Critic was complaining about when it talked about “at: 1” instead of “first”:
strikeFrameArray ^Array with: rolls removeFirst with: (rolls at: 1) with: (rolls at: 2)
In Smalltalk, instead of saying rolls at: 1, you could say rolls first. That’s considered better style. In the case in hand, however, since there is also a rolls at: 2, you could argue that it should be left as it for symmetry. Some of the other uses, though, probably should be changed. For example:
spareFrameArray ^Array with: rolls removeFirst with: rolls removeFirst with: (rolls at: 1)
In that one, there’s no excuse for not saying first. So let’s change it:
spareFrameArray ^Array with: rolls removeFirst with: rolls removeFirst with: rolls first
That’s much more idiomatic, and as you can see, it saves some parens as well. At one point when I was programming yesterday, I actually thought of using #first, but since I had the at: 2 to worry about, I didn’t. It turns out that #second isn’t defined for OrderedCollections. However … there’s nothing stopping us from doing that!
In Smalltalk, essentially all the source code is there. Here’s the code for OrderedCollection>>first:
first "Answer the first element. If the receiver is empty, provide an error notification." "This is a little faster than the implementation in the superclass." self emptyCheck. ^self basicAt: firstIndex
Now I happen to know that the method #basicAt: is a primitive. That is, it is implemented inside the virtual machine. If we wanted to implement the method #second, we could readily do so, by just adding that method to OrderedCollection. Often in Smalltalk, it’s useful to extend the language in that sort of way. There are issues with it however. One is that when you get a new version, you have to install your extensions again, and while that’s usually straightforward, sometimes it isn’t. So one proceeds with caution in doing things like that. For tonight, we won’t.
What about that other Critic comment, “Utility Method”? What’s that? Code Critic is referring to this method:
frameScore: anArray ^anArray inject: 0 into: [ :sum :each | sum + each ]
What’s wrong with this? Well, this method doesn’t send any messages to the object it’s a member of, nor does it refer to any of the object’s instance variables. It has no reason to be in this object at all! This is a code smell, and it was nice of the Critic to point it out for us.
What is this smell trying to tell us here? Well, we have created an Array containing all the rolls of interest to a given frame of the game. Then we sum it up. Whenever you create an Array in Smalltalk, there’s a good chance you’re looking at an object trying to be born. Here, maybe there is a Frame object trying to come into being. Let’s push on that possibility a bit and see what happens.
Framing the Question
Remember the #score method:
score ^self frames inject: 0 into: [ :sum :eachFrameArray | sum + (self frameScore: eachFrameArray)].
This code contemplates a collection of frame objects, but it turns out that they are arrays, not frames. If they were frames, the code could look like this:
score ^self frames inject: 0 into: [ :sum :each | sum + each score ].
See? If frames were objects, we would assume that iterating a thing called frames would produce instances of Frame, and we would just call them “each”, instead of “eachFrameArray”. I used that name because the each argument wasn’t what one would normally expect, namely a Frame. And if it was a Frame, we could send a score message to it and get the Frame’s score. Well, OK, let’s just leave the code that way and make it work. We’ll make it work as quickly as we can, then see about cleaning it up.
Maybe it works now. I’ll find out by running the tests. Well, unfortunately not. They all get the same error: Array does not understand #score. Now we could implement score on Array, but that would definitely not be the done thing: we wouldn’t put a domain-specific method on a generic object. We need a Frame. I’ll create a Frame class. Nothing special about it – I could have waited until I needed it, which I will as soon as I change the #frames method mentioned above:
BowlingGame>>frames ^(1 to: 10) collect: [ :frameIndex | self frameArray ]
We’ll make this code return a collection of Frames. And my plan is to create each Frame with its frameArray inside it. So …
frames ^(1 to: 10) collect: [ :frameIndex | Frame new: self frameArray ] Frame class>>new: anArray ^self new setRolls: anArray Frame>>setRolls: anArray rolls := anArray
Let’s pause here for discussion, though we’re not quite done. We change the #frames method to collect ten new instances of Frame. Each Frame is created using new: self frameArray, so we’re basically just passing the frame array of rolls that we already create into the Frame object. The class method #new: shows how we construct the Frame, and setRolls: is the conventional pattern for an initializing setter. (An ordinary setter for rolls would be called #rolls:.)
So at this point we should be creating valid Frames, and our tests should fail sending score to Frame. Let’s find out … sure enough, that’s what happens. Want to see?
Well, then. We need to define #score on Frame. That looks like this:
Frame>>score ^rolls inject: 0 into: [ :sum :each | sum + each ]
I expect this to make the tests run … and they do!! We remove that utility method #frameScore: and run the tests again. Still good.
So. Are we done? We got rid of the utility method, but we replaced it with a new class with three methods on it, #new:, #setRolls:, and #score. Is this a good thing? Well, maybe yes, maybe no. The most important thing to learn here is how easy it was to do that. It looks like the same number of steps as doing it would have been in C# or Java, but really it wasn’t. We didn’t have to do any type definitions, cast anything around, change the type return of the frames method, and so on. Just did it, and Smalltalk guided us all the way.
Right now, I’m not convinced that Frame has made things better, but it feels like a step in an interesting direction. The tests are green, it’s after 11 PM, and I think I’ll get some sleep. See you next time!
Oh, I mentioned an idea. Too late now – I’ll tell you next time.