Category Archives: Ruby

My Full-Circle Journey Back to Smalltalk

I suppose it’s time to tell my story. I was a Smalltalk zealot in the late 90’s, but I left it behind when I started my own business. I’ve now come full-circle, and I’m finding once again that Smalltalk is the best tool for most of the programming work I do.

I learned Smalltalk programming from Travis Griggs while working at Key Technology. He mentored me in Smalltalk and other general programming methods, and I introduced him to Linux. Travis likes to give me more credit than I deserve for some of the work that we did together, but one thing was certain: we had a blast working together.

Key went through a difficult aquisition, and I spent most of my energy working to bring our two software teams to common ground. My wife and I travelled back and forth between Medford, Oregon and our home in Walla Walla (with our newborn son, our first) while I worked with developers in both locations. Diplomacy is not something I particularly enjoy, but I’m good at it and the team really needed somebody in that role.

But the end result was that I didn’t get much time to do any significant programming. The more time passed, the more I missed it. I didn’t get to work with Travis as much anymore, and our team was becoming fractured as our project list grew. Despite all of my efforts to bring people together, our team was dissolving under the increasing project load.

I had always wanted to work from home, and I was becoming burned out in my Key position, so I left Key and took up consulting work. Most of my clients needed web applications, so I became a web developer.

I built my first web applications in Smalltalk. Travis and I had written the original WikiWorks code, and I used our experience there to build my own HTTP 1.1 server. This later became the basis for the Swazoo server, which was built on my code at Camp Smalltalk 2000 in San Diego. I implemented my own Smalltalk Active Pages (abbreviated SAP, to distinguish them from ASP sites) and built several sites using them.

At the time, there wasn’t much web application work being done in the Smalltalk world, and the frameworks people were developing were unattractive to me. I had to support myself, from top to bottom, and this was starting to feel painful.

This was what drew me towards PHP, Apache, and PostgreSQL. The promise of safety in numbers was attractive — I would never have to wonder if my server was fully compliant with the HTTP specs. But the main attraction for me was PHP’s support for for various open source libraries. PHP made it easy to handle image uploads in multiple formats, draw custom graphs, and build other visuals on the fly. Editing PHP apps in vim was easier over low-bandwidth connections (like a cell phone data connection) than making a VNC connection to a headful server image, too.

So I left Smalltalk behind and built a few large PHP apps. Nearly all of them relied heavily on PHP’s graphics library bindings for generation of custom reports and editable page components. I built content management systems that let my customers edit their page titles, for example, and behind the scenes PHP would generate new navigation buttons from template PNGs and TrueType fonts. Another app managed a database of donors for a non-profit radio network and allowed call-center volunteers to easily enter pledges from supporters — while the studio crew saw up-to-the-minute totals on their screens.

Things went smoothly at first but got rougher as the applications got bigger. I made heavy use of the flimsy object model in PHP 4 and built my own object-relational layer, and this helped me survive as long as I did. But I grew increasingly frustrated as I tried to scale my PHP applications. There was no way around the ugliness of the code.

seaside.jpgAt that time, somebody pointed me at one of the early versions of Seaside. I had taken a look at Seaside a few years ago, and while it looked interesting, I confess that I didn’t get it. I saw how call: and answer: worked to give you a certain level of modalism in your application, but that was all I saw. The Seaside HTML writers were less mature then, too, and that was a deterrent to me. I liked being able to interleave PHP and HTML code in template files, but that was mostly because my pages didn’t use CSS effectively.

I missed the larger picture, so I went back to PHP.

When Ruby on Rails started to gain popularity, it was like a breath of fresh air to me. Rails is a very clean framework for web applications, and it required no paradigm shift. Much of the work I had done in PHP already followed some of the Rails patterns, but now I had a full-fledged object-oriented language at my disposal again. I thought I had finally found the best of both worlds — a thriving community, lots of support for graphics formats, easy database connectivity, and a nice clean dynamic language.

These days, that would sound like the end of the story. But it’s not. The truth is, I’m disappointed with Ruby. Rails is “classic web development done very cleanly”, as Ramon Leon says. And Ruby is my favorite non-Smalltalk language. But it’s not Smalltalk. In fact, I’ve come to see that Ruby missed the mark on several points.

  1. File-based source control. In a Smalltalk image, all of the classes and code libraries your application needs are available to you immediately. Tools like the Refactoring Browser are much harder, if not impossible, in Ruby.
  2. Reflection. Ruby’s reflection is OK, but it’s much weaker than the reflection in Smalltalk. If more of the Ruby base code was itself written in Ruby, and Ruby allowed better reflection, a Ruby code browser would undoubtedly be easier to write (and more powerful once written).
  3. Speed. The Ruby interpreter is slow. In Rails, I still end up writing more complicated database queries than I should have to, because the Ruby interpreter is so slow to process data in memory.
  4. No Keyword Messages: Travis and I ran some tests using keyword-style messaging using Ruby’s last-argument-collapsed-as-hash, and the performance was terrible.
  5. Blocks. Ruby simply missed the mark on on these. All blocks should be first-class objects, not just a layer of syntax.
  6. Live Interaction. Irb is good, but Smalltalk workspaces are much better.
  7. Debugger. The Ruby debugger feels like gdb compared to Smalltalk. Most of the time, people opt to debug using console output and interactive exploration using Irb. It’s better than printf, but not much.
  8. Class Library: The Ruby class library is much younger than the extensive class library in Squeak or VisualWorks.

As far as Rails is concerned, I have far fewer criticisms. But once you’ve looked at Seaside, and you really understand the approach Seaside is taking, Rails just looks old. I never want to marshal data through a URL again if I can help it.

I’ve also come to see that unless you’re a light user of a technology, you have to be able to support yourself. This is not a downside for Smalltalk, it’s a reality of the complex environment we work in. Smalltalk’s web and graphics tools have come a long way lately, but ultimately Smalltalk empowers you to support yourself in a way that Ruby just doesn’t. I have been digging in the Ruby 1.8 source tree more often than most, and it’s not nearly as easy to navigate as the Smalltalk base. Remember, most of the Smalltalk base is implemented in Smalltalk.

Seaside is certainly more advanced than Rails in terms of raw technology, to the point that it’s hard to wrap your head around the concepts. It took me two attempts, and I’m fairly young and intimately familiar with Smalltalk. Rails is smoother — it gives a better out-of-the-box experience in the short term. But for larger web apps, you need to be thinking further than that.

Don’t be surprised if Seaside just looks weird to you at first. Leave it for a little while and then come back. You’ll be glad you did. And when it clicks for you, beware. You won’t ever look back.

13 Comments

Filed under Rails, Ruby, Seaside, Smalltalk

Identity and Equality in Ruby and Smalltalk

One important concept in object-oriented languages is the difference between equality and identity. The concept isn’t complicated, but the English words we use to talk about it are imprecise. The thesaurus says that “identity” and “equality” are synonyms. So let’s back up and make sure we’re being clear on the concepts.

We often talk about variables as if they “hold” a certain object, but that’s not a very good metaphor. You can put the same object “inside” any number of different variables. If we’re working with containment as a metaphor, we run into problems here. It’s as if we’re saying that the same person is in two different houses at the same time.

Instead, we need to think of variables as names. Different people use different names for the same thing (Puma, Mountain Lion, Cougar) or the same name for multiple things (my “home” is not the same house as your “home”). Names are just a reference to something real, a way to address things.

We say that two variables are identical when they both refer to the same object. If they refer to different objects that represent the same value, we say that they are equal. Consider, for example two points that have the same X and Y coordinates. They are equal, but not identical. Objects themselves can tell you whether they’re identical or equal.

In Smalltalk, the relevant messages are:

= The objects are equal
== The object(s) are identical
hash A number that must the same for all equal objects, typically used by collections

The Smalltalk virtual machine is the sole place where object identity can be determined, so the #== method is implemented as a primitive and never overridden by any class. But scores of classes implement their own method for #= and #hash. A classic Smalltalk programming mistake is to override #= but not #hash, thus breaking the requirement that two equal objects have the same hash value.

In Ruby, there are actually significantly more equality messages that you may encounter. The definitions I’m using here come from pp. 95 and 571 of Programming Ruby:

== Test for equal value
=== Used to compare each of the items with the target in the when clause of a case statement
<=> General comparison operator. Returns -1, 0, or +1, depending on whether its receiver is less than, equal to, or greater than its argument.
.eql? True if the receiver and the argument have both the same type and equal values. 1 == 1.0 returns true, but 1.eql?(1.0) returns false.
.equal? True if the receiver and argument have the same object ID
hash Generates a Fixnum hash value for this object. This function must have the property that a.eql?(b) implies a.hash == b.hash.

This is a little overwhelming. Ruby’s :equal? method is a test for identity, and like Smalltalk, it is implemented in Object and never overridden. That gets us started.

The methods for :eql? and :hash are probably the most similar to Smalltalk. If you override one, you need to override the other. Ruby’s collections use them to determine equality for things like Array#uniq or Hash lookup keys.

This has bitten me more than once. I’ve implemented a new sort of object and overridden ==, only to find later that

  • I can’t use those objects for Hash keys, and
  • Arrays return unexpected results when sent :uniq

It seems that instead of overriding ==, I should have overridden eql? and hash. This makes my Hashes and Array#uniq results work like I expected. But wait… The default Ruby implementation of == defers to equal?, which is not what I want either:


>> g1 = GridAddress.new(:key)
=> #<GridAddress:0xb7b569c8 @key=":key," @row="nil">
>> g2 = GridAddress.new(:key)
=> #<GridAddress:0xb7b50028 @key=":key," @row="nil">
>> g1.eql?(g2)
=> true
>> g1.hash == g2.hash
=> true

>> g1 == g2
=> false

There’s no deferral from one of these messages to another. In other words, the behavior I want requires me to override eql?, hash, and ==. These are independent, parallel implementations. The difference is primarily semantic. With eql, there is no attempt to coerce the objects to a similar type. Methods for ==, on the other hand, typically try to coerce objects to the same types first before making comparisons.

Finally, we come to the “spaceship” operator, <=>. This is used by a mixin called Comparable, and you automatically get various other comparison operators (including ==) if you include Comparable and implement <=>. The only objects that understand it are those for which less-than and greater-than comparisons actually make sense.

In my opinion, Ruby complicates things here with little payoff. Most programs compare using ==, and the details of other equality comparisons are lost on most Ruby programmers. I think it’s a fair generalization that most Ruby developers don’t use their own objects as hash keys, probably due to a sort of bias towards the more “primitive” object types like Ramon Leon talks about (see “Obsession with Simple Types” in this post).

It’s also worth noting that if you override eql? or ==, you’re expected to check to make sure that the objects have the same type before you start comparing any details. This is the common pattern in Smalltalk, too. Most Smalltalk implementations of #= first check in some way to see that the two objects are the same kind of thing and then proceed to compare relevant details.

6 Comments

Filed under Ruby, Smalltalk