Version 1 of Spot

Updated 2011-04-21 09:45:04 by koen

Koen Van Damme -- This is an experiment in object-orientation. I wanted to find out the minimal number of classes required to pull an object system out of the ground. It turns out that you need only 2:

  • Object, the class of all objects, top of the class hierarchy.
  • Meta (also often called Class), the class of all classes. So every class is an instance of Meta. And since classes are objects, Meta inherits from Object.

As usual in Tcl, we store an object's data in a global array. A class is a special object with special entries in this array. All the array contents can be changed dynamically, so any normal object can be turned into a class and vice versa.

Above I said that Meta inherits from Object. It would be better to say that Meta chains to Object. There is no inheritance; instead, objects chain to one or more other objects. When you invoke a method on an object, the object first tries to find the method locally, and then simply passes it on to its chains one by one. When a class creates a new object, it makes itself the single chain target for the new object. But again, you can change all object data dynamically, so you can just set another target or add new targets.

The whole system boots itself from only Object and Meta and their methods.

Note that this is only an experiment, not a fully functional object system. E.g. it does not support constructors, destructors, object persistence etc. You could easily add those features if you wanted to.

Here is an example of how to use it:

 class Animal {
    # Use the normal 'set' and 'proc' to define class members.
    set num_legs 4
    proc walk {} {
       puts "$this walking on all [$this get num_legs]s..."
    }
 }

 class Cat : Animal {
    set tail_size 5
    proc sound {num_times {sound "Meow"}} {
       for { set i 0 } { $i < $num_times } { incr i } {
          puts "$sound!"
       }
    }

    # Class-static variable: just prepend 'static'.
    static set typical_names {felix fluffy lucky}

    # Class-static method: just prepend 'static'.
    static proc info {} {
       puts "INFO: Cats are animals with 4 legs, a tail, and lots of hair."
    }
 }

 ### Use static members by calling the 'Cat' object, which happens to be a class.
 ### Note that 'Cat get' will be resolved to 'Object:get'.  In other words,
 ### objects inherit their 'get' method from the top of the hierarchy by default.
 Cat info
 puts "Some typical names for cats are: [Cat get typical_names]."

 ### Printing a class: this shows the internals for the 'Animal' and 'Cat' class.
 ### The output should give you an idea of how the SPOT object model works.
 Meta:print Animal
 Meta:print Cat

 ### Create cat 'felix'.
 Cat new felix
 Meta:print felix
 felix sound 3
 felix walk

 ### Inject a new class into the chain,
 ### to change the "inheritance" dynamically.
 class Silly_walker {
    proc walk {} {
       puts "$this doing a silly walk"
    }
 }

 felix set class "Silly_walker"
 puts "Felix now chains to: [felix get class]"
 felix walk   ; # This now produces a silly walk rather than the one from the Cat class.

 ### Inject a new class which re-implements the 'get' method, to block access to member variables.
 class Blocked_getter {
    proc get {var_name} {
       return "$this has no access to $var_name"
    }
 }

 puts "Getting 'num_legs': [felix get num_legs]"
 Silly_walker set chain "Blocked_getter"
 puts "Blocking 'num_legs': [felix get num_legs]"

 ### Clean up by deleting all arrays.
 puts "Spot arrays: [info vars spot*]"
 Meta:del felix
 Meta del Cat
 Meta del Animal
 Meta del Silly_walker
 Meta del Blocked_getter
 Meta del Object
 Meta del Meta   ;# Yes, we can delete 'Meta' itself.
                 ;# Note that the 'Meta:xxx' procs are
                 ;# automatically removed too:
 #Meta:message "This is impossible" ;# -> TCL ERROR

 puts "Spot arrays at end: (should be empty) [info vars spot*]"

Here is the code:

(NOT YET)


Category Object Orientation