Dwemthy's Array in Smalltalk

Ruby Is Smalltalk Minus Minus:

"--- Challenge to Smalltalk advocates: go ahead and reimplement DwemthysArray? then! The Array is a rather short program so it shouldn't take you too long. It's also rather well known, even celebrated, by now so a ST version will reach many people who wouldn't normally even notice ST advocacy. And an elegant implementation will frankly be a lot more convincing than 'sure, we could do that' assertions. We've been promised Smalltalk DWEMTHY before: http://redhanded.hobix.com/inspect/theRabbitWillDieInSmalltalk.html - who will deliver?"

BIG DISCLAIMER UPFRONT: This is just an exercise and I am not advocating Smalltalk over Ruby or Ruby over Smalltalk. Both have their strong points. I am just doing this out of curiosity and for fun.

I was browsing around the web and glanced upon the snippet above from the c2 wiki. I am not sure how long it has been up there (probably since 2005) but it seems that no one has responded to it. For those unfamiliar with it Dwemthy's Array it's a Ruby meta-programming example that is along the lines of a text-based game. I was familiar with it when it first surfaced but never thought of the need to implement it in Smalltalk since the approach is not very Smalltalk-ish in the first place. However, since I had some time, I decided to explore how it could be done in Smalltalk by mimicking the Ruby implementation.

Dwemthy's Array in Smalltalk
A sample run of Dwemthy's Array in VisualWorks. Look at the Browser to see how I mimic the Ruby code. Notice that I still need to explicitly write the super initialize.

The gist of the Ruby implementation of Dwemthy's Array is the ability to write code like the following to declare a new class. It's simple and it reads like a mini Domain-Specific Language. Because of this, it also looks very appealing aesthetically.

class ScubaArgentine < Creature
  life 46
  strength 35
  charisma 91
  weapon 2
end

Basically it can be "de-sugared" to look like this which would make it more familiar. Basically the entire code relies on doing class method calls (the self.life) to create

# This is a comment
class ScubaArgentine < Creature # This declared ScubaArgentine as a subclass of Creature
  self.life(46) # This is just a call to the class method life i.e. Creature.life(46)
  self.strength(35)
  self.charisma(91)
  self.weapon(2)
end

The gist of how all of this is done is in the following snippet:

# Advanced metaprogramming code for nice, clean traits
def self.traits( *arr )
  return @traits if arr.empty?

  # 1. Set up accessors for each variable
  attr_accessor *arr

  # 2. Add a new class method to for each trait.
  arr.each do |a|
    metaclass.instance_eval do
      define_method( a ) do |val|
        @traits ||= {}
        @traits[a] = val
      end
    end
  end

  # 3. For each monster, the `initialize' method
  #    should use the default number for each trait.
  class_eval do
    define_method( :initialize ) do
      self.class.traits.each do |k,v|
        instance_variable_set("@#{k}", v)
      end
    end
  end
end

# Creature attributes are read-only
traits :life, :strength, :charisma, :weapon

In my opinion, the original article on Dwemthy's Array doesn't actually explain how things work clearly -- the author goes through great lengths to make it seem magic and the prosy language doesn't help clarify how it works underneath. Unfortunately, if I were to dissect it line by line it would make this article extremely long. So, instead, anyone interested in a better explanation so watch this video presentation or refer to the Pickaxe Ruby Book or the Ruby Way book.

In the following paragraphs, I am going to focus on what I think are the most fascinating issues of the implementation and how it could be done in Smalltalk. The following paragraphs assume familiarity with Ruby and Smalltalk reflection/code generation. This lecture slide from the University of Bern gives a quick refresher on Smalltalk reflection. My implementation is a direct-translation as close as I know how to implement it. So that means that I will be sticking to code generation, using class instance variables and a lot of class methods.

Dynamic code generation

Ruby offers method such as instance_eval and class_eval to make code generation slightly simpler. In addition, it also offers attr_accessor. Smalltalk, on the other hand, does code generation by using the compile: 'some smalltalk code' method. Code generation might be a misnomer since that is basically how everything is defined into the image.

The compile: 'some smalltalk code' is used behind the scenes in Smalltalk for generating accessors and doing other refactorings. I have not actually seen the instance_eval and class_eval used to do any refactorings.

Code generated in Ruby is also hidden meaning that you are not able to see the actual source/implementation of the generated method. This makes things more terse but can make understanding/debugging the program harder -- it really makes things much simpler if you could actually see the methods that are generated.

define_method( someName ) do |val|
  @traits ||= {}
  @traits[someName] = val
end
actually generates the following simple code:
def someName(val)
  @traits ||= {}
  @traits[someName] = val
end
But you never get to see that code!

Every generated code in Smalltalk (using the compile: 'some smalltalk code') appears in the Code Browser like every other method, which brings me to the next point...

File-based vs. image-based implementation

Because Ruby operates in a file-based manner, it re-initializes the meta-programming facility every time you reload the file. For this form of programming, reloading the file has some advantages -- if you screw up you can just easily reload everything and not worry about side effects.

As I was doing the implementation in Smalltalk, I screwed up a couple of times and had to remove some of the generated code. Rather than unloading everything and loading it again (tedious and not very Smalltalk-ish) I implemented a reset method that removes all the dynamically generated code and resets other meta-programming constructs. So using my reset method, I can simulate starting from a clean state each time for testing purposes.

Additionally, one solution that I used to simulate file-based loading is to override the initialize method on the class side so that some action is performed as the code is loaded into the image.

In a nutshell, it is possible to simulate a file-based loading by writing proper reset and reinitialize methods. These methods definitely come in handy as I was doing this exercise since I don't need to unload/load the parcel in each time -- I did do that for the final testing to ensure that everything is working.

Dynamically Adding Instance Variables

In Ruby it seems that you can just define a new instance variable using @some_name or instance_variable_set("@some_name, some_value)and it will immediately be picked up. In Smalltalk, I had to ensure that I declared a new instance variable first using self addInstVarName: 'someName' and then I can change its value using self instVarNamed: 'some_name' put: some_value.

Instance variables:

"Instance variables do not need to be declared. This indicates a flexible object structure; in fact, each instance variable is dynamically appended to an object when it is first assigned."

This two-step approach in Smalltalk hints are the differences on the underlying layer on how instance variables are stored.

Class Instance Variables

Another important (and possibly subtle) point about the approach is the use of class instance variables. Class instance variables offers the ability of class variables without the problems that come about from subclassing. This is important because it allows classes to share common variables without interference from the class hierarchy.

By default, VisualWorks uses/offers class instance variables when declaring a new class. If you ever need a class variable then I think you would actually define what is called a shared variable.

Bottom line, class instance variables are really useful. Without them you would have to resort to a lot of trickery to share data between instance of a class while preventing access/modifications from subclasses.

Class Methods

The Ruby implementation above uses a lot of class methods. Class methods reside on the metaclass of a class. Fortunately Smalltalk follows this paradigm closely so it's not hard to implement these features. It just involves doing compile: 'some smalltalk code' on the right receiver. In my implementation, you will find a lot of the methods in the class side of things. Most of the instance methods are dynamically generated.

Implicit self

Ruby's implicit self does have some advantage here since it reduces the need to repeat self keyword over and over and over and over....



So here's my implementation in VisualWorks Smalltalk. I am not an expert in packaging parcels in VisualWorks so expect potential problems. However, even if you decide not to load it into VisualWorks it is possible to eyeball the code to get the gist of it. As far as I know, this is not a very Smalltalk-ish way to approach the problem. I did it this way to mimic the Ruby implementation as closely as possible.

The Ruby implementation seems very magical because a lot of the generated code is actually hidden. I think that if it were possible to just examine the generated code, it would make it must easier to analyze and reason about. It also enables you to use your normal tools for refactoring/ debugging. Of course, because things are hidden, it also makes the code terse and sometimes easier to read especially if you don't care about the details.

For an alternative approach see Darren Hobbs: Getting Meta. I am not really sure whether his implementation works in the end or not.


comments powered by Disqus