Over my many (10) years of programming experience (mostly C/C++, but also Forth and lots of assembly) I had slowly come to completely distrust object-oriented programming. OOP is a tool. It can help in some domains, but can also cause more problems than it solves in many others. All too often, some co-workers and I find ourselves chuckling and commenting, “I’ve C++’ed myself into a corner again.” Ah, the allure of OOP is great.

Now, before I get comments from OOP fanatics and C++ gurus telling me how we do it “wrong,” let me assure you that we don’t. In the world of game development, the twin evils are Design and Fun. Both of which cause the requirements of the game to change radically from day to day (and sometimes from hour to hour). Because of this, development needs to be fast, fast, fast. This idea was best expressed by Chris Uppal in a comp.lang.smalltalk post:

> Make it work.

> Make it right.

> Make it fast.

Chris disagreed with this when he posted it, but that’s okay. In games, making it work is most important during early development. It’s all about prototyping. The designers and artists need to see what it’s going to be like before deciding to keep it, throw it away, or do something different. With a heavy OO philosophy, a programmer will spend far too long working out the perfect class hierarchy just to get a triangle drawing on the screen. And God forbid when the designer sees it, he decides that he’d like stars instead. Suddenly half the code is now useless (and the other half needs restructuring to be useful).

Alright. So what’s this got to do with Smalltalk? Well, I was skeptical. I was still curious, but I really didn’t want to spend my free time I had relearning a language that was all about something I was almost entirely against.

But then it occured to me — what if the philosophy (OOP) wasn’t the problem, but the implementation (language) was? Admittedly, almost all OO languages in existance are derivatives of C++. But, Smalltalk is very different! Also, Smalltalk was the first (well, second, but who’s counting?) OO language. Certainly any inherent problems should have been solved by now. Perhaps there was a gem here, and I just wasn’t giving it the complete attention it deserved.

I decided to write a complete program in Smalltalk. And just to give myself an added challenge, it had to be a game: a complete DirectX driven game with sound, graphics, controls, etc. To be honest, I didn’t care about the result, what I cared about was how it got there. Could it interface well with DirectX? Was there an advantage to the Smalltalk environment (Forth and Lisp both wowed me with their interactive developing)? And most importantly, would “true” OOP be a benefit or a hinderance?

For the first 2–3 weeks of playing with Dolphin, I was clearly unimpressed. The community was great (everyone at comp.lang.smalltalk[.dolphin] was extremely helpful). When learning Forth and Lisp, the biggest hurdle was getting around the community to ask questions. On comp.lang.lisp, try asking “how do I get the type of a variable?” and you’ll get a barrage of snide replies reminding you just how naive you are, because “variables don’t have types; values have types!” Of course, this has nothing to do with the evaluation of a language, but certainly made the experience a more pleasurable one.

That said, I was still unimpressed. In all the toy problems I’d try to learn the language in more depth, I kept coming back to my original concerns. I just couldn’t see anything that would be helpful. In fact, I had an ah-ha! moment when I discovered how to actually define global constants in Smalltalk. Something as simple as a constant needs to be a string object, in a pool object, in a singleton Smalltalk object. And that felt like some serious over-engineering to me. But I decided to chalk this up to the fact that I still hadn’t done anything of any reasonable size and still didn’t understand the language all that well.

And about one week later something happened. I don’t exactly recall how it happened (reading a post or just trying something out), but I learned something that wasn’t in any Smalltalk tutorials on the web (note to Smalltalk tutorial writers: this needs to be there):

Everything in Smalltalk is an object!

Okay. That’s in every tutorial to Smalltalk on the web. However, those tutorials don’t show you just how far the rabbit hole goes! Let me explain. Coming from C++, a class has a constructor: the place where data is initialized. Likewise, in Smalltalk, objects have an instance method called #initialize. However, in C++, one uses the new operator to create an instance of the class, and it (in turn) calls the constructor for me. That isn’t the case in Smalltalk. In Smalltalk, when you define your class, there is suddenly a new object created in the image. That object is the “class object” for that class. The class object is a kind of singleton where class data and methods are.

So? How is this different from static data and static methods in a C++ class? That’s what I initially wondered as well. And let me tell you, there is a world of difference. But let me reiterate again this key point: when you use the class name in your source code, you are sending a message to an object! In Smalltalk, if I type:

ShellView new

This calls the #new method of the class object for the ShellView class. ShellView is not a keyword or a type. It’s an actual object, and you are sending it a message. That message is resposible for the creation of an instance of the ShellView class, and returning it. This is very important. Because each class has a class object, this gives Smalltalk some unprecedented reflection capabilities. And I’m only just beginning to scratch the surface of them. As an example, open up the Worksheet; we’re going to try a few things:

ShellView allSubclasses

There is a list of all the class objects which constitute the classes that derive from ShellView.

ShellView allInstances

There are all the instances of the ShellView class that are currently in existence right now.

Pretty slick. And that’s just 2 of the many functions that class objects have available to them. Experienced programmers should already be drooling. This alone provides some impressive power. But let’s show a practical use for someone still a little confused or not yet convinced.

In my game engine, I have certain functionality that is required in a subclass of ShellView that I need the window to have. So I created GameView, a subclass of ShellView. However, I don’t want the end programmer to actually use GameView. Instead, I want them to subclass it and override some important functions that describe the behavior of the window, etc. At the same time, for various reasons, I don’t want the end programmer to actually create an instance of this view (for DirectX reasons I’d like only one at a time to ever exist).

So, what I was able to do was in my GameEngine object, I created the method #createGameWindow:fullscreen:. This method’s first parameter is a class object, which is of the GameView subclass you want to use. But this poses a problem: how can I be sure that the class object passed is a subclass of GameView? After all, I need to make sure that it will work. However, this is a trivial problem to solve:

self assert: [class inheritsFrom: GameView].

Done. The C++ solution would have been to have the end programmer create the view and pass it in. However, the Smalltalk method has an added bonus: I don’t have to create the window right then! I can just hold onto the class object and create it later when I really want to. Or, I might never create it (in the event that some other initialization code failed).

The reflective power of Smalltalk is awesome.

Alright. You get it. The reflective power of Smalltalk is awesome. This was my “the sleeper has awaken” moment in Smalltalk. And consequently, one hour later I purchased a copy of Dolphin X6 Professional. But this still didn’t answer the question of whether OOP would hinder more than help in the development of a game, and subsequently, whether the OOP problem was one of philosophy or implementation.

At this point, I think it’s fair to say that if I hadn’t purchased Dolphin, I would have thought that OOP was more of a hinderance than a help. And this isn’t because it’s true. It’s because the professional version of Dolphin comes with some very nice features, most importantly the IdeaSpace. Up until this point, I had painstakingly been putting together Direct3D and DirectInput wrappers for Dolphin. Not only did I have no less than 7 Dolphin windows open at once at any given time, but no matter how friendly the UI was, jumping around between objects, copying, pasting, package browser, etc., was becoming a monstrous headache. The IdeaSpace made that headache go away with a single click. To anyone on the edge of purchasing Dolphin, just know now, the added features in the Pro version are well worth the purchase price.

Back on point. One of the major reasons someone can “C++ themselves into a corner” is that if their class hierarchy is wrong (and it always is), re-architecting can be a major hassle. And if you discover the problem(s) well into development, you may just be stuck with them. Likewise, whole companies have succumb to the “rewrite it right” bug (summary: in rewriting, you lose a lot of fixes). So, does Smalltalk suffer from this as well?

Honestly, I don’t know. Going into this, I would have felt very comfortable saying “yes.” But there have been a few things happen that are making me wonder.

As part of my “make it work” step, the first thing I wanted to do was get a 2D texture on the screen. Without going into morbid details, this required a specific vertex format (one of many) with a RHW vector component, texture coordinates, diffuse color, etc. Once I got the texture rendering, it was now time to implement the other vertex formats. However, in a perfect world, the old format would be a subclass of one of these new ones. Behold, all I need to do is drag the old class onto the new one, it’s now subclassed. I rename it to a more appropriate name, and Dolphin opens a new window showing me every place in the code that referenced the old name so I can change them promptly.

Later, in further development of the game engine, I have no less than 5 times changed hierarchies and inserted new objects to simplify the current (working) ones. These changes have taken minutes (not hours as it would have been in C++). I found that the “make it right” step wasn’t so clearly a distinct step from “make it work” any more. Once the code was working, I was immediately able to make it right.

From past experience programming GameBoy Advance games in Forth, I know that interactive development is a monumental win for any programmer. I’ve already been able to have my game engine up and running, and change the render loop on the fly and see updates without ever stopping execution. And while the engine is running, I can inspect it at any time and modify any data within it, all without bringing down the program. While this has nothing to do with OOP, it certainly is a testament to Smalltalk (and to a great implementation).

– Jeffrey Massung

I’ve also republished another one of Jeffrey’s articles: “Smalltalk: An Application Language.” Wonderful piece!