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 TObject
s, 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