What Is Smalltalk Programming?An interesting, albeit abstract, discussion came up recently about the fundamental nature of Smalltalk programs—should they be thought of as a set of definitions or as a sequence of transformations applied to a particular starting point? It’s a discussion with a rich history in the Smalltalk community, and there isn’t an easy answer. The traditional Smalltalk model is transformation-based. We work in an image and program by writing new classes and methods, modifying existing ones and executing code snippets. As we accept each change or run each do-it, the system we’re working in is changed, and those changes have an immediate effect. If we’ve saved the code out and file in it, it’s in a series of “chunks,” and each chunk is applied one at a time. Our changes are recorded in a change log in sequence, and when we save the image, we create a new basic state for the system. When we start up again, that state is re-created. In some sense, a Cincom® VisualWorks® or Squeak image is just the same image from long-ago Smalltalk-80, modified and saved over and over again. One of the major innovations of Digitalk’s Team/V was that it was based on a declarative model of a Smalltalk program, and the tools manipulated this model rather than the image directly. To load the code in an image meant to apply that model into a particular system. This enabled some powerful capabilities. With the appropriate tools, you could build up an image from scratch, making the resulting image both smaller and more predictable as to its contents. The development environment could be separated from the deployment image, you could more easily make major changes to the way the system is implemented and so on. This “set of definitions” model, which isn’t as strong as calling it declarative but is a step on the way there, is more like programming in traditional languages, where we have a set of text files and a compiler that processes them. The ordering of definitions in them doesn’t matter. Smalltalk programming has been more like an operating system in that it retains its configuration when restarted, and changes to the system are made in a running system and may require some code to be run at carefully chosen points while updating. Both approaches enable interesting things. Having an image with live objects in it that we transform, and not just a static code base, gives us a lot of interesting capabilities. For example, the requirement to have the debugger, compiler and tools written in Smalltalk and running in the same set of live objects they’re working on improves the overall capability of the language because everything has to be able to work with any of the different layers of turtles. A concrete example of that is Smalltalk’s exception-handling model. In Smalltalk, searching the stack for a handler and unwinding the stack are two separate phases. In many other languages, the two happen together. But if they’re done together, by the time you have found the handler for an exception, or the fact that it doesn’t have a handler, the stack has been unwound, and you can’t debug the process anymore. So in Smalltalk, the default behavior for an unhandled exception is to put you into a debugger.
“I remember my reaction when first working with Java and realizing that an unhanded exception not only didn’t put you into the debugger, it would throw you out of the debugger if you were in it. You could work around this in Java by putting a breakpoint into the constructor of an exception, but that means you’d get a debugger every time the exception is thrown, not just when it wasn’t handled. So exceptions that were thrown a lot but usually handled were a real pain to deal with.” – Alan Knight, the Cincom Smalltalk Engineering ManagerOn the other hand, having a more traditional programming model makes other tasks easier, for example, loading code or upgrading to a new version of the system. A file-in is a sequence of statements, but it’s not the same sequence of statements you used to create your program; it’s one that’s generated, and it may not be in any particular order. That’s a problem if you care about the order of the statements. But nowadays most people don’t use file-in anyway; they use mechanisms like parcel loading in VisualWorks that treat the code as a set of definitions, though they allow you to add code to be run at different points in the load. ENVY had “atomic loading” a long time ago, Store has it now and Monticello may also have it. These are all team programming tools. One reason this is an important feature of them is that with multiple people working at once in different images, it’s difficult to even define a single order for changes. On the other hand, some things do critically depend on ordering. For example, it’s not that unusual to define a compiler to allow different syntax in Smalltalk methods. Examples of that include things like VisualWorks’ DLL/CC that lets you write C definitions in Smalltalk methods, and Cincom® ObjectStudio® 8 that loads a new and subtly different compiler to let you load and run ObjectStudio code on top of an underlying VisualWorks runtime. So when we load, say ObjectStudio 8 from Store, we need to load the new compiler, but we need all of that done and completely live before we can start compiling code that uses it. So, which of these approaches represents the essence of Smalltalk programming, and which of them defines a Smalltalk program? Neither. Smalltalk programming is the act of writing a program in Smalltalk, however you go about it. And what’s a Smalltalk program? It’s a group of Smalltalk objects executing code somewhere. How those objects got the way they are doesn’t matter; the result is still a Smalltalk program. In fact, even in some of the systems being used for comparison, the situation isn’t as clear-cut as it sounds. The order of definitions in the text files that define most programming languages do matter in some situations, like C macros. And in operating systems, some changes are applied live, but other changes are applied sort of live but then require the system to reboot and have more work done during image startup when the system is only semi-alive. Most of the time, most of the people writing Smalltalk programs don’t think about the order in which they’ve done things, and don’t want to have to. They want to be able to load their code into a new image, even a new image that’s from a different version of the base, and have it just work. But every now and then the ability to make use of the live system that’s underneath this and control the way it’s transformed can be very powerful and is something that should not be lost.