Updated 2013-12-08 22:25:34 by RLE

The following code is somewhat experimental. I have seen quite a few variations on this theme. However, I believe this code does things in a new and interesting way. This code is not feature complete, it is primarily a proof of concept at this point. Please feel free to make comments and/or criticize this code. If you have feature/code suggestions please feel free to add them to this page. If you would like to submit code changes, please email them to me and I will review them for addition.

This code allows for the following concepts/constructs:

  • "classes" definitions or "what procs and variables do all instances of this class have?"
  • "objects" or "an instance of a class."
  • Now supports transparent usage of class variables and procs from inside the class.
  • "interfaces" or "what procs are required to implement this interface?"
  • "comment" blocks inside class and interface declarations.
  • "options" or "what optional features are enabled for this class or interface?"
  • "single inheritance" or "procs from this other class are now in this class."
  • "multiple inheritance" or "procs from these other classes are now in this class."
  • "nested inheritance" or "procs from the base of my base are now in this class."
  • "constructors" and "destructors", that can take parameters and can prevent the object from being created/destroyed.

The only "missing" things that I can think of so far are:

  • Being able to delegate a method to one of your bases.
  • A nice way to handle "conflicts" between procs in the immediate class and the bases (perhaps in more than one base). Currently, if a proc exists in the immediate class, it is always called. The result of calling a non-qualified proc that exists in more than one base class is undefined.
  • The ability to control access to the procs of a class (public, private, etc).

This code is now too big for a WiKi page (about 100K, including test files).

It may be downloaded from: http://www.tclbridge.com/tcl_oo.zip (link is dead, I need to find that now somewhat obsolete code and put it back up -- JJM).

Here's a simple example of how to declare and use a class with this OO system...
 class demoClass {
   option alias.primary
   option proc.trace.outer
   option proc.trace.inner

   variable x
   variable y
   variable z
   variable array_variable

   comment {
     #
     # override global proc locally...
     # (notice we call the global proc from inside it)
     #
   }
   proc class_output { output } {
     ::class_output "$this: $output"; # calls global proc
   }

   proc constructor { object args } {
     class_output "NEW: $this"; return "1"
   }

   proc destructor { object args } {
     class_output "DELETE: $this"; return "1"
   }

   proc doNothing {a} {}

   proc simpleReturn {} { return $x.$y }

   proc returnMethod {a} { return [doNothing $a] }

   proc callMethod {a} { $this doNothing $a; return $x }

   proc testArray {} {
     set array_variable(1) "element1"
     set array_variable(2) "element2"
     set array_variable(3) "element3"
     return [array names array_variable]
   }
 }

Written By Joe Mistachkin
 Version History

  • 19/Nov/2002 Version 1.00 -- initial release.
  • 19/Nov/2002 Version 1.20 -- added transparent access to class variables.
  • -- streamlined some of the code.
  • -- more comprehensive cleanup of variables, procs, and namespaces.
  • 20/Nov/2002 Version 1.25 -- more robust handling of invalid class declarations.
  • -- added tests for arrays inside classes.
  • 20/Nov/2002 Version 1.30 -- more argument validation.
  • -- added more tests dealing with invalid class declarations.
  • -- added class_objects proc to return a list of active objects for a class.
  • 20/Nov/2002 Version 1.31 -- fixed error handling for a certain test case involving bad class declarations.
  • 20/Nov/2002 Version 1.32 -- fixed problem preventing multiple matches in class_objects.
  • 22/Nov/2002 Version 1.40 -- added support for interfaces.
  • -- added support for comment blocks inside classes and interfaces.
  • -- cleaned up and organized tests.
  • -- changed some error messages.
  • -- other miscellaneous changes.
  • 13/Mar/2003 Version 1.60 -- added support for inheritance, single and multiple.
  • -- a lot of cleanup and reorganization.
  • -- added support for implementing an interface using base classes.
  • -- added more introspection features.
  • -- added support for parameterized constructors/destructors (can stop creation/destruction).
  • 13/Mar/2003 Version 1.61 -- corrected loop invariant for interface definitions.
  • 15/Mar/2003 Version 1.75 -- massive internal changes to namespace handling.
  • -- added support for nested inheritance.
  • -- added first stages of "conflict resolution" (currently, there is none).
  • -- cleaned up class_hasProc.
  • -- massively changed and streamlined class_findProc.
  • -- added more introspection commands.
  • -- added more tests to "class_test2.tcl".
  • 16/Mar/2003 Version 1.89 -- more massive changes to namespace handling.
  • -- added support for class and interface "options".
  • -- removed some old dead code.
  • -- changed proc aliasing to be more robust, aliases are now visible within the namespace of the calling object only.
  • -- added support for a variable number of arguments to procs defined with an "args" parameter as the last parameter.

Wishlist:

  • source version diffs
  • typecasting and checking
  • runtime casting
  • a 'self' keyword
  • message passing notation over any object to object link
  • streaming objects and hierarchies, and maybe as simple form list contents
  • class library for referencing UI elements and structures...

I first thought I'd be joking, but I just saw there is a large source file to implement all the well known oo features. Good reference list. Good also to decide without much trouble that oo is a rather limited extra as compared to well defined source code buildup and data structures, and in fact nearly always generates a price to pay in less efficiency of the code (it never adds something that makes our processors tick through the essential algorithms faster) and it always adds overhead in source and usually object code size (re-usability of functions or procedures works just fine with traditional functions, just reuse them..), and the structure of ordering things in one or multiple trees hierarchy is somewhat practical, but not as standard ordering paradigm more than in functions in groups.

Not so much intended for the author; I did years of research fruitfully using an oo language, and I generally like the idea that everything works through list commands of solid structure and maintainability, like in tcl, except when one needs the added speed of extra compiled code. It serves at least as great instructional value, and can be a hell of a lot safer to use and overseeable to maintain and debug.

JJM 09/Apr/2003 -- I appreciate your feedback. Let me see if I can address your wishlist one by one:

  • source version diffs
  • -- I am not sure how this would be useful.
  • typecasting and checking
  • -- This feature will be included in the next release, time permitting.
  • runtime casting
  • -- This feature will be included in the next release, time permitting.
  • a 'self' keyword
  • -- Already provided via "$this" and "$me" from inside the class.
  • message passing notation over any object to object link
  • -- Could you expand upon this a little bit, I'm somewhat confused.
  • streaming objects and hierarchies, and maybe as simple form list contents
  • -- Are you referring to object persistance support?
  • class library for referencing UI elements and structures...
  • -- This would be something that the end-user of the package could build with my OO system.

I could imagine when sending messages between objects that the addressing of objects, or maybe of uniquely referencing to a certain object in a certain class could include using the (class hierarchy based) path to that object:
   [send target_class.target_subclass.targe_tobject message]

Streaming would indeed suggest the possibility of persistence, though I didn't mean that, I just meant that at least at times it would be good to see the structure of the instantiated object tree printed somehow, and possibly get some file savable or probably even better concept 'listable' representation of the data in some subset of all objects. Like a favorite of mine would be to send the root object (if there is one) the message 'save' and that one would get a list returned where each object has a sublist which for instance lists its variable contents.

The fun of tcl is that, as I wrote, I was being sort of funny-like about for instance type casting: since mostly everything is a list, there is not much point in casting a list to a list 'formally'; practically of course one can do what seems fit for a certain purpose....

escargo - I know I took the "message sending to be of the nature of Smalltalk message sends. Certainly the notion of having self be a specially value relates to that as well.