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.