Choosing Smalltalk On Porpoise

Dolphin Smalltalk is widely regarded as having the most polished IDE of all the Smalltalk variants. By virtue of being a Windows-only product, the vendor Object Arts had the freedom and flexibility to highly optimize the Smalltalk environment, adding unique extensions and providing buttery smooth integration. Arguably, it’s the most productive of all the Smalltalk products.

One developer, a senior engineer at Disney, wrote about his experiences with Dolphin. His words are most insightful and therefore I’ve published an excerpt from his article.

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!

When I started this project, the largest concern I had was that forcing anyone to use object-oriented programming would be extremely limiting or even detrimental to the final application. I’ve had some very bad experiences with OOP in the past: lots of hidden execution time and gross obfuscation of — and sometimes impossible to follow — code. OOP is a tool, and sometimes an extremely useful one. But, is it one that should be used exclusively?

My theory was that perhaps OOP wasn’t the problem, but rather the implementations that I had used (namely C++ and its derivatives). Perhaps Smalltalk, the grand-daddy of OO languages had it right. Fast forward 7 months….

A typical program might begin with a few modules of “super” code — code that can do anything and everything. Then, as the program develops and molds, functionality is extracted and moved into new modules. As this continues, the code becomes easier to read, maintain, and extend. This is a good thing.

One of the many lessons learned (the hard way) by good programmers is knowing when to stop. Eventually a point will be reached when the returns are minimal, and if continued, the code may become so obfuscated that maintaining and extended (and performance) can be severely impacted.

The OO paradigm can have a way of clouding the judgment of otherwise excellent programmers with the allure of “perfect” code. The kind of code where everything is in its place, only knows about what it needs to know about, all arrows point in one direction, and hides 90% of what all other code shouldn’t be concerned with.

Sadly, that kind of code doesn’t exist in the real world. It only exists on paper. I have yet to see any significantly large MFC program where the following macros (or similar functions) weren’t declared globally:

#define MY_FRAME ((CMyMainFrame*)AfxGetMainWnd())
#define MY_VIEW ((CMyMainView*)MY_FRAME->GetActiveView())
#define MY_DOC ((CMyDoc*)MY_VIEW->GetDocument())

And then, little by little, the document class starts making assumptions about the view, and now someone adding functionality down the road is in a lot of trouble.

Let’s just clear the air right now: arrows only pointing one direction are a great starting point, and should be maintained as long as possible, but someday your application needs to ship, bugs need to get fixed, and the end user is never going to care that your rendering engine just happens to know about this one special level. The end user will care, however, if it can’t render the level properly or efficiently.

Surely, hiding data and code is good, though, right? Well, do you want to just trust that the interface library you just plugged into your code base isn’t using 10x as much memory as needed, leaking half of it every frame, and slowing down the rest of your application? I didn’t think so. And neither do I.

Performance is always a concern — especially in games. And I get sick to my stomach when I purchase a new 2D, turn-based strategy game like Civilization IV at the store and find that it recommends:

  • Pentium 4 CPU with at least 1.8 GHz
  • 512 MB RAM
  • 128 MB graphics card
  • 1.7 GB free hard disk space

Seriously, we’re talking about a 2D, turn-based game! Oh, and my laptop beats those specs, and it still runs slow! Maybe Firaxis shouldn’t have just trusted some of the 3rd party code they used.

OOP is a tool, and can be very useful. But it, like Mr. Worldly Wiseman, has a bad habit of “promising” the removal of a great burden from the programmer: having to write real code at the end of the day. For some reason, many programmers still think that with the proper framework and a double-click, their application will just appear.

Now, the curtain lifts, and in walks Smalltalk…

- “What? What’s gonna happen?”
- “Something wonderful!” (from 2010: The Year We Make Contact)

Let me state this unequivocally: Smalltalk is an application language! It’s about creating applications; it’s not about creating code. And there is a world of difference between the two. It’s about removing the mundane barriers from the programmer so that he or she can focus on the real deal, get it done, and move onto the next task.

Smalltalk dispenses with the “arrow” notion. (This is the term I use for those pretty code design diagrams that show object hierarchies and what systems know about, and how all the arrows should only point in one direction). That’s left up to the programmer. It’s definitely wise to design up front, and write the code as cleanly as possible for as long as possible. But, when it’s time to swing the hammer and make it work, Smalltalk doesn’t make a programmer jump through hoops to get the job done.

Nothing is hidden. Nothing. Not even the compiler. Programmers like Ian Bartholomew can create profilers that allow profiling every line of my application — including the core Smalltalk image — without having to write one extra line of code. Anyone can modify the core libraries to add, improve, or even completely remove functionality if and when it’s needed.

Most languages that pride themselves on enabling the programmer to develop applications quickly are founded on the premise that programmers can’t be trusted. Visual Basic and C# both jump to mind immediately.

But, when it’s time to swing the hammer and make it work, Smalltalk doesn’t make a programmer jump through hoops to get the job done.

These languages fall terribly short after the first 30 minutes of use. This is usually due to one, simple development truth: time constraints mean that prototypes have a nasty habit of turning into the final application. How often have you had to fix a bug or add a feature in an existing C# application at work, only to open it up and see this?

public partial class Form1 : public Form {
private void button1_Click(…) {
// …

The default names given to forms, buttons, and other common controls haven’t been updated. The original programmer was just trying out an idea that the boss then liked, but there wasn’t time to go back and do it right. So, now you are wasting time trying to work around default code that is already difficult to follow, because you didn’t code it to begin with.

Perhaps you even want to take [what should only amount to] 30 seconds and fix the problem by updating the name from button1 to SendButton, but soon give up when you realize that changing the name doesn’t update the auto-generated function names, but did move it from a public place to a private place when it was renamed, and there are several places in code where button1 is directly referenced. Another 30 minutes wasted, and now enough code has changed that QA should probably have another round with it. More money wasted.

And that isn’t even the worst case scenario. The worst case is when the button name was set originally, but its text and implementation are now completely different from the original name given (the SendButton is actually the CheckSpelling button). Now it’s harder to maintain, and a good programmer will feel morally compelled to fix it.

These are problems that seriously impede the development of applications. Applications that reduce overhead and iteration time, generate revenue, and give the programmer weekends with the family. An “application language” should free the programmer of such tedious, downright ridiculous responsibilities, without treating the programmer as a miscreant who doesn’t know how to properly manage code.

This is what Smalltalk does for me. I only write the code that matters. If I need to rename a class, all references everywhere else in my sources are automatically updated for me. If I change a method name, a browser window opens allowing me to see every caller so that I promptly fix them. These are just two of many, many, many features, all possible thanks to the reflective nature of Smalltalk, due in no small part, to its object-oriented approach to problem solving.

I have learned to embrace object-oriented programming [in Smalltalk]. When it is implemented well (dare I say “properly?”), this paradigm doesn’t just constitute another tool in the toolbox; it is the toolbox, from which everything else stems.

Since I started this endeavor to create a 2D game engine in Smalltalk, I’ve not once — wait, let me reiterate this — not once have I stopped, and started over again. I’m still working in the same image I started with in late August, 2006. All my designs began with prototypes (*cough* hack *cough*) inside the main code base, and yet the code is clean. When they worked, it took minutes to reorganize and re-factor the code properly. And when they didn’t, it took minutes to strip out bad code, and restore the old.

I’ve honestly never had more fun programming! Smalltalk has actually made programming more fun than it already was. It allows me to iterate the code so fast! The speed at which I can try an idea, see what’s wrong, switch gears, or continue molding until it’s right is so ridiculously fast, that it’s fun. It’s been stated that programming isn’t fun. Problem solving and code design are fun. Programming in Smalltalk is just that — all the time. It’s fun.

– Jeffrey Massung