This week I wound up trying to put together a sample “real-world” problem suitable for use in an automated web thing aimed at potential new hires. Of course, any actual “real-world” problem would be have too many extraneous details to make it suitable given the constraints of the web thing, but trying to create the illusion of real-world-ness was kind of fun.
Since this seemed like sort of a weird way to spend a day or two, I decided to make it weirder by implementing both my solution to the problem and the code to generate test data in a language I don’t use every day. Just for the heck of it, I picked Smalltalk (Specifically, Pharo), the first time I’ve written Smalltalk code in anger for… hmm. Let’s see… I wrote the intro chapter to this in about 2000, and even then it had been a couple of years since I really wrote something in Smalltalk. (For the record, my other choice was Clojure, but I seem to have some weird mental block about getting started on a Clojure project. Someday soon, though.)
Anyway, Smalltalk was interesting because the environment is so different from anything else going and because I really haven’t used it in so long, I was curious to see whether my reactions to the Smalltalk environment would be any different given how long it’s been. For example, the last time I worked in Smalltalk, SUnit and TDD wasn’t a thing.
Overall, successful experience. I was able to actually write the program. It probably took me a little longer than it would have in Ruby, just because I was a bit rusty and the Pharo UI was a little unfamiliar. But it works, and most of the code isn’t bad – it got a little ragged toward the end. Here are a few random notes about the experience:
Whatever the line between a “scripting” language and a “normal” language is, Smalltalk is on the “not a scripting language” side of it. Smalltalk is very much a purist language with a small, consistent syntax, and not a lot of the sugar that you get used to in the Ruby/Python/Perl neighborhoods. Method names tend to be longish, and although the environment encourages short methods, basically there’s more typing in Smalltalk than in the equivalent Ruby.
Smalltalk was created almost completely outside the C/C++/Java family of programming languages, which combined with its pure structure makes it feel somewhat alien. And I say that as somebody who likes Smalltalk and genuinely enjoyed working in the environment.
* The first element of an array is index 1, not index 0. Which, honestly, makes more sense.
* Smalltalk doesn’t have operator precedence, and is evaluated strictly left-to-right, so a mathematical expression like
2 + 3 * 4 will not give you the result you would get in, say, every other programming language ever.
* Smalltalk doesn’t use the dot-notation for method calls, instead, the dot is a statement separator. It has keyword arguments, as later borrowed by Objective-C. Strings are single quotes, double quoted strings are comments.
Then you get other things like all the boolean and loop logic being managed by the library rather than the syntax. Really, the only thing the syntax manages in Smalltalk is order of operations and a very small amount of literal syntax. There’s a rich set of collection classes, and the names and classes are just slightly off from what you are used to. Takes some immersion to get the feel.
The other really unfamiliar thing about Smalltalk is the environment and workflow. Smalltalk source isn’t normally kept in files. Instead, you run a binary image of all the code in the Smalltalk environment. If I’m remembering correctly, a Smalltalker working on multiple projects would have a separate image for each project. (There are mechanisms for teams working together, but I don’t think they are very standard, and I’m not all that familiar with them.)
The thing about Smalltalk is that you are always inside the environment. The idea, for example, that Ruby is super-flexible in what it allows you to do at run-time is a trivial point in Smalltalk. Everything is at run-time, because the phone call is coming from inside the house, so to speak. Of course you can create classes and methods at run-time. If you couldn’t, the whole system would be unworkable.
The image contains your code, the entire Smalltalk system, and the state of all the objects therein. Smalltalk typically also has a mechanisms for export/import of source, and also for internal version control. I think it’s fair to say that dealing with the workspace is at best counterintuitive when you are used to dealing with text editors and files.
Your main interface with the Smalltalk system is the code browser, which allows you to view any class and method in the system. One method at a time. Quick Quiz – what’s the Smalltalk keyword to define a method? Trick question. Smalltalk doesn’t have one. Smalltalk just knows that the things you type into the code browser are methods, and the first line of a method is its name. Code browsers are very neat, but again, using them effectively is a skill – a big step is realizing you can have more than one of them open at a time.
What’s interesting to me are the places where the Smalltalk environment is still ahead of my regular code environments, and where it’s behind. On the plus side:
- When you save a Smalltalk method, the code browser verifies that it’s syntactically correct before allowing the save. If you are calling a method or using a variable that doesn’t exist, you have to okay it – in some circumstances, you even have the ability to create the method or variable as part of the save process.
- The browser has easy access to all old versions of a method that you are looking at.
- It’s very easy to see places that might call the method you are writing.
- The browser also has some powerful refactoring functionality built in.
- Integration is amazing, since everything is running. To give one example, the code browser knows which methods are SUnit tests, and they display in the browser with a red/yellow/green/gray indicator of their current state (error/failure/success/not run). Individual tests can be run separately easily in the browser, and since they environment is already loaded, the tests run in an eye blink.
- On a related note, the debugger is famously awesome. From a breakpoint, you can see the state of all variables, edit values, edit code, all nice and easy.
- You also can create workspaces, which are just blank code windows that you can execute arbitrary snippets in, like a iRb loop, but a bit more flexible.
But there are some missing pieces:
- The code editor itself isn’t as powerful as you are used to if you’ve been using Vim or TextMate or, hell, anything. I’m a big “typing is not the bottleneck” kind of guy, but still, I would have loved some macros or snippets or something.
- The one-method on display per code browser limitation is, well, limiting. You can have multiple code browsers open, but that takes up a lot of real estate. Tabbed browsers, split windows, that kind of thing would be kind of nice.
- Intellectually I know that it’s really hard to lose changes in a Smalltalk image, but it still feels fragile to me not to have files readable by an external tool. UPDATE: I could have been clearer about this. Smalltalk doesn’t save changes in the image as such, instead it saves all your changes and lets you play changes back. I was trivially able to recover about 30 minutes of work after a Pharo crash, simply because all the changes were recorded and easily placed back in the image. The Smalltalk setup is effective, but feels different from the way I tend to think about their projects.
One interesting feature of the browser based editing environment is that it’s very easy to create a new method that is based on an existing method. You don’t even have to cut and paste, just browse to the existing method, change its name and save. Smalltalk will create a new method based on the new name.
This is very nice from a getting code in the system standpoint, but it seems to have the side effect of encouraging more duplication in the system than I might be otherwise comfortable with. The effect is confounded by the fact that you can only see one method at a time in the browser, making it difficult to scan for duplication.
What’s not getting across here is how smooth the workflow in the small is when you really get the rhythm going. It’s an environment that really suits a “make a small change, evaluate the change” process. In some ways, it’s not surprising that the test-first movement would have gotten serious momentum in Smalltalk, test-first is just a formalization of the way a good Smalltalk programmer interacts with the system. Plus, I’m a purist in so many ways, and even though the code takes a little getting used to, I like the way it turns out.
So, successful test. Would try again. I still want to try something with Seaside one of these days just to see what that’s like.