Getting meta

Okay, so the investigations continue. As mentioned previously I’m attempting to reimplement Dwemthy’s Array in Smalltalk. There are a couple of ways to port code from one language to another.

One approach is to attempt a ‘direct translation’, changing as little as possible outside the syntax. This has the benefit of making it easier to mentally flip back and forth as the code is ported. The difficulty depends on how similar the features of the 2 languages are. For example, transliterating idiomatic Lisp code to, well frankly, any non-Lisp based language would be exceedingly challenging. There is also the risk of being sucked into having to reimplement a bunch of the standard libraries of the source language in the target language.

The other approach is to establish a high level picture of the intent of the original code, and reimplement that in terms of the target language. The benefit here is being able to take full advantage of the idioms of the target platform. The disadvantage is obviously that subtleties of the original behaviour may be lost in the conversion.

Imagine a conversation conducted using a translator. A word for word translation might be more accurate but probably makes less sense in the target language, while a translator who digests an entire sentence and reiterates it in the colloquial form of the target language might well make more sense to the listener, but there is an increased need for the translator to understand the subject under discussion.

For the purposes of my journey I’m taking the direct translation approach. As Dwemthy’s Array uses some pretty exotic features of Ruby this is probably the more challenging of the two approaches, but that just makes it more interesting.

First off, I think I need to be able to extend the subclassing functionality of VisualWorks. The standard system creates subclasses by sending a message to a NameSpace object. This is a problem as I want to intercept the call for a specific class (Creature) and do some extra stuff. The current VisualWorks code looks like this:

defineClass: className superclass: superID indexedType: typeName private: isPrivate
instanceVariableNames: iVars classInstanceVariableNames: ciVars
imports: pools category: category attributes: attributes

| superclass approved class cbr mbr |
superID == nil
ifTrue: [superclass := nil]
ifFalse: [superID binding isForClass
ifTrue: [superclass := superID value]
ifFalse:[self error: (#SuperclassMustBeAClass <>
'The superclass of a class must also be a class')]].
Behavior checkLegalBehaviorType: typeName.
approved := SystemUtils
validateClassName: className
for: nil.
approved == nil ifTrue: [^nil].
cbr := (BehaviorBuilderRecord forName: approved in: self)
superclass: superclass;
instVarString: iVars;
category: category;
attributes: attributes;
behaviorType: typeName;
importString: pools;
private: isPrivate.
(mbr := MetaclassBuilderRecord new)
forInstance: cbr;
instVarString: ciVars.
class := ClassBuilder new
addRecord: cbr;
addRecord: mbr;
reviseSystem.
^class

What I actually want is for most of this code to live in the Class er, class so I can hook it from a subclass. The first chunk is all about making sure the superclass of the class under construction exists, so I don’t need that. I think I can take all the code from the line that starts ‘Behaviour’ and drop it into the Class um, class more or less verbatim, flipping around the references to ‘self’ and ‘superclass’ and adding ‘aNameSpace’ parameter:

subclass: className nameSpace: aNameSpace indexedType: typeName private: isPrivate
instanceVariableNames: iVars classInstanceVariableNames: ciVars
imports: pools category: category attributes: attributes

| approved class cbr mbr |
Behavior checkLegalBehaviorType: typeName.
approved := SystemUtils validateClassName: className for: nil.
approved == nil ifTrue: [^nil].
cbr := (BehaviorBuilderRecord forName: approved in: aNameSpace)
superclass: self;
instVarString: iVars;
category: category;
attributes: attributes;
behaviorType: typeName;
importString: pools;
private: isPrivate.
(mbr := MetaclassBuilderRecord new)
forInstance: cbr;
instVarString: ciVars.
class := (ClassBuilder new)
addRecord: cbr;
addRecord: mbr;
reviseSystem.
^class

The code in NameSpace then changes to:

defineClass: className superclass: superID indexedType: typeName private:
isPrivate instanceVariableNames: iVars classInstanceVariableNames: ciVars
imports: pools category: category attributes: attributes

| superclass |
superID == nil
ifTrue: [superclass := nil]
ifFalse:
[superID binding isForClass
ifTrue: [superclass := superID value]
ifFalse:
[self error: #SuperclassMustBeAClass <> 'The superclass of a class must also be a class']].
^superclass
subclass: className
nameSpace: self
indexedType: typeName
private: isPrivate
instanceVariableNames: iVars
classInstanceVariableNames: ciVars
imports: pools
category: category
attributes: attributes

Important thing I learned: an instance method of class Class is inherited as a class method of normal classes.

Minor aside: here I am changing the way classes are defined. I’d have been up to my armpits in the guts of the VM by now if this was Java.

Dwemthy’s Array Sidebar 1

Hmm. VisualWorks has namespaces, which Smalltalk-80 does not, and they seem to have changed the way subclasses are defined. According to my copy of the purple book, a subclass is created by sending a class the message ‘subclass:’. It should be possible to implement a version of subclass in the Creature class that adds the extra code.

However, VisualWorks wants me to send ‘defineClass:’ to a namespace. Problem with that is I want to change the way subclasses of a particular class are defined. If I change the code in the NameSpace class that will affect every class that is ever created thereafter.

I hope VisualWorks hasn’t sacrificed this flexibility. I think that the original implementation was the correct one (classes create their children), and that it should have been possible to introduce namespaces without changing it, or at least get the method in NameSpace to delegate to the original code. Currently both mechanisms exist, although the original one appears to have been left there just for backwards compatibility.

Hopefully James or someone who knows more about VisualWorks than I do will read this and set me straight.


Added later: James happily responded. How many other product managers would personally respond to some random blogger’s idle meanderings?

Exploring Dwemthy’s Array (Part 1)

Dwemthy’s Array is a great example of the metaprogramming capabilities of Ruby.

At the heart of Dwemthy’s Array is the Creature class, and at its heart is the following code:

def Creature.traits( *arr )
return @traits if arr.empty?
attr_accessor *arr
arr.each do |a|
class_eval %{
class << self
def #{a}( val )
@traits ||= {}
@traits[#{a.inspect}] = val
end
end
}
end

This code declares a static (class) method that, when called with a list of symbols, creates a class method for each symbol that, when called, lazily instantiates a class variable, with the symbol’s string as a key and the method’s argument as the value.

The next chunk looks like this:

class_eval %{
def initialize
self.class.traits.each do |k,v|
instance_variable_set( "@" + k.id2name, v )
end
end
}
end

This section defines an instance method called initialize that will iterate through the list of traits (stored as a class variable) and create instance variables for each entry, named after the key, assigning them to each value. The initialize method is called by default when an object is instantiated.

In a nutshell, each subclass of Creature provides a prototype template that allows multiple instances to be instantiated with copies of the same data. The Creature class also allows for easy addition of new traits. Calling the traits method more than once will append the new values to the collection.

There’s a line in Creature like so:

traits :life, :strength, :charisma, :weapon

Creature and all its subclasses share the above common traits.

One of the subclasses, Rabbit, starts like this:

class Rabbit < Creature
traits :bombs
life 10
strength 2
charisma 44
weapon 4
bombs 3

It adds an additional trait called ‘bombs’. and sets the initial trait values for its instances by calling the class methods defined by the meta code in Creature.

One important point to note is that the ‘traits’ class variable is defined per-subclass, so each subclass (subclass, not object instance) gets its own local collection of traits.

Okay. Now I think I understand how it works. So can it be done in Smalltalk? I don’t know yet.

That’s Part 2.

The wheel: version 2.0

Sometimes it seems that the world can never have too many:

  • Bug tracking tools
  • Email readers
  • Wiki engines
  • Message boards
  • Blog engines
  • RSS readers

I don’t mean ‘instances of”, rather the number of different software implementations. I’ve lost count of the number of times I’ve heard or read (and thought or said), ‘I tried out foo the other day but it didn’t work exactly how I wanted so I thought I’d have a go at writing my own….’.

I take some heart from the fact that the majority of the above list (which was developed by the highly scientific method of me thinking about it for about 30 seconds) consists of communication tools. Forming connections with other people is clearly a basic human drive.

Every method is an interface

Another way of thinking about the difference between dynamic and static languages is how they express interfaces. For example, Java interfaces define a group of method signatures that a class must implement. There is no way of enforcing that different subclass implementations do roughly the same thing – its down to convention and good style.

A dynamic language such as Ruby or Smalltalk doesn’t have the concept of interfaces. Instead, each method signature could be considered as implicitly defining its own interface – an interface containing just that one method. When calling a method on an object, the object’s type is essentially irrelevant. All that matters is that it has a method whose signature matches the one you’re trying to call.

Great things I’ve never done

I regularly have ideas that are impossible, or forget within hours, have no clue how to implement, or no time, or all of the above. I really should make a list.

  • A squeak wrapper around the Gecko HTML rendering engine.
  • A simple, high quality probabilistic gossip based P2P framework (I actually started a Java version but got bored).
  • A tuple space implementation on top of the aforementioned simple P2P framework.
  • A mobile-code mechanism for the tuple-space, so arbitrary tasks can be submitted. The Jini model uses jars downloaded from a webserver, which isn’t really ‘grid’ enough somehow.

Programming as Literacy

In what’s typically called ‘the western world’, before literacy became essentially ubiquitous it was limited to a select few. Monks originally, then scribes. These individuals possessed a truly remarkable skill. They could make marks on paper that could remember things. They could capture information and retrieve it later. Associated with this skill was the ability to make marks that could perform complex computations and produce an answer. People with these skills were hired by nobles and merchants to increase business value.

You know what I’m going to say next of course.

Programming appears to have many parallels with literacy in medieval times. It is a somewhat arcane skill known to a select few, who typically put that skill to use for others in return for remuneration. There are very few scribes left nowadays, although I’m sure at the time they felt that as there would always be a need to write things down, that there would always be scribes.

It’s not technical

A counterpoint to this post from my colleague Sam.

It’s not technical. Languages don’t succeed for technical reasons. If this were true we’d all be programming in Lisp and Smalltalk. Java became popular by riding the internet (specifically the web) wave. .Net became popular because Microsoft have billions of dollars to spend on marketing. Both of these platforms have something in common – they are aggressively marketed by a big industry name.

Write once, run anywhere. Sun coined the term, and successfully won the mindshare. Developers working in Lisp & Smalltalk wondered what was so special about that.

Garbage collection. Most work in garbage collection was done in the 70’s and 80’s (possibly even earlier – Lisp has been around since the 60’s). Again marketed heavily by Sun, while the aforementioned Lisp and Smalltalk programmers looked on nonplussed.

Lisp didn’t take over the world because (to my knowledge) there was no big name vendor marketing it, and everyone outside the Lisp community thought it was just for AI.

Smalltalk didn’t take over the world because of infighting between vendors, and a failure to see the threat of Java. They were too busy competing with each other to notice that Java was about to swamp all of them.

Having said that, there are places where Smalltalk and Lisp are thriving, and those companies are more than happy for everyone else to remain distracted by Java and .Net while they consistently outperform them in time-to-market of new features.