leah blogs

March 2005

30mar2005 · The T Object-System (in Ruby)

The T programming language is a dialect of Scheme developed during the 1980’s at Yale university. In it’s manual, it says:

T is a dialect of Lisp derived from Scheme. It is comparable in power and expressivness to other recent Lisp dialects such as Lisp Machine Lisp, Common Lisp, and NIL, but fundamentally more similar in spirit to Scheme than these other Lisp dialects.

T is an attempt to create a powerful and expressive language which is portable, efficient, and suitable for a broad variety of applications, including symbolic, numerical, and systems programming.

T draws from many sources in its attempt to provide a complete yet simple and unified system. For the most part, the language’s designers are collators, not originators. Common Lisp, NIL, and Scheme have been the strongest influences.

The manual contains a lot of interesting stuff, the one I found most interesting however is T’s object-system. This object system is so easy (one could say trivial), but still so powerful and flexible that you just need to admire it.

Basically, there are two procedures to implement the T object-system, OBJECT and OPERATION. OBJECT takes two parameters, a value proc that gets called if the object itself gets called (I leave that out in the Ruby version, since it only makes sense with Lisp-1s and rarely has an use anyway), and a list of handlers, each element consisting of an symbol and an lambda. OBJECT returns a new object that responds to the handlers given.

In my Ruby re-implementation, we would write:

# An object responding to `op'
obj = TObject.new(:op => lambda { |s| 34 })
obj.op  # => 34

The first parameter of the lambda is used to pass self, but you can pass several arguments, of course:

# An object responding to `op'
obj2 = TObject.new(:op => lambda { |s, x| x })
obj2.op(55)  # => 55

As you can see, we already have polymorphism. So far, this is not very spectacular, though. The really interesting things about the T object-system are operations and joins.

T operations are like methods of Object in Ruby. They are used as a default everywhere except you did override them (In my Ruby reimplementation, operations are only defined for TObjects, not for every datatype as in T):

# An operation to do `op'
TObject.operation(:op) { |s|
  :zebu         # By default, return :zebu
}

TObject.new(:op => lambda { |s| :quagga }).op  # => :quagga
TObject.new({}).op                             # => :zebu

So far, the object-system is rather boring, there is no real instantiation, no mixins, no singleton classes, all the cool stuff from Ruby is lacking. :-) Enter JOIN, it takes an unlimited number of objects and combines their behavior:

foo = TObject.new(:foo => lambda { |s| :foo })
bar = TObject.new(:bar => lambda { |s| :bar })

joined = TObject.join(foo, bar)

joined.foo          # => :foo
joined.bar          # => :bar

Now, I can’t think of anything (except method_missing, which easily could be handled as a special-case) that’s possible in Ruby’s object-system, but not T’s.

(You may argue that the T object-system lacks instance variables, but those can easily be replaced with accessable closures, which both T and Ruby have.)

Another nice thing about the T object-system is that it’s soo easy to implement (20 lines of Ruby), that you could quickly write it in low-level lanuages as C as a foundation for a future programming language.

By the way, here is the T object-system in Ruby:

class TObject
  attr_reader :handlers
  @@operations = {}

  def self.operation(name, &block)
    @@operations[name] = block
  end

  def self.join(*args)
    TObject.new(args.inject({}) { |a, e| a.merge e.handlers })
  end

  def initialize(handlers)
    @handlers = handlers
  end

  def method_missing(name, *args)
    (@handlers[name] or @@operations[name]).call self, *args
  end
end

NP: Interpol—Next Exit

Copyright © 2004–2022