Minimal OO requirements for Tcl

Purpose: to define what a 'nice object system' should look like, if included in the Tcl core.

Initially, here are some comments, dated Jan 22, 2001, by Donal K. Fellows:

  [email protected] wrote:
  > According to William H. Duquette <[email protected]>:
  > :For Tcl to catch up, it needs two things:  a nice object system,
  > :and a substantial standard library that's pre-installed.
  >
  > Okay, let's start the work on defining what these two things should look like,
  > what gets included, work out the details to get these to build cross platform
  > and DO IT.

Agreed. (And I agree with Will; those two are enabling technologies, much more so than many other things.)

  > 1. What constitutes a 'nice' object system?  What needs to be present, what
  > shouldn't be present?  What could we put into Tcl to make the jobs of the
  > N Tcl OO extension writers easier and that, when used, won't irrevocably
  > break Tcl for them?

I think that whatever we settle on, it should be class-based and it should have support for both anonymous and named objects. Objects must be able to have both methods and variables. It is probably a good idea to have constructors (I've worked without them, and it is a pain) but I've no idea if we need destructors or just plain deletion traces. No method, constructor or destructor invokation that causes an error inside itself should make that error vanish. It should be easy to vwait or attach using -textvariable to an object variable.

Why class-based? Because that's what almost everyone wants in an OO system. I know they aren't strictly needed, but meeting everyone's expectations here is probably a good idea.

Why both anonymous and named objects? Both have their uses; named are at their most useful when working with interactive input, and anonymous are ideal inside scripts, particularly if they can be automatically garbage collected (though that requires substantive improvements to the interpreter.)

Why constructors? Well, if you omit them, you just have to reimplement them in an ad hoc fashion. :^(

Why not destructors? It all comes down to whether a destructor can be called multiple times and whether it can abort object destruction. By comparison, an object deletion trace has none of this nasty conceptual baggage.

Why must errors not vanish? Debugging. 'Nuff said. Any exceptions? Class deletion. Maybe.

Why easy variable traces/linking? Experience shows that this is a very useful feature indeed, particularly for properties dialogs. No sense in requiring yet another ad hoc reinvention of the wheel.

I'm not at all sure that class "static" methods and variables are a good idea. Maybe, but maybe not. They can cause a lot of trouble. Allowing objects to have extra variables not listed in the class definition seems to be a much more useful feature to me, and more in keeping with Tcl's current philosophy, as expressed by procedures.

We need good introspection; listing of all classes, listing of all objects in a class, listing of all methods in a class, listing of all variables in an object, etc.

And whatever we do, it should become the One True Way Of Doing Objects In Tcl. Anything else will just produce yet another irritatingly incompatible OO extension. Like we need extra.

  > 2. What constitues a 'substantial' library?  I assume this must be some
  > number of functions larger than tcllib, right?  How many?  And, next, as
  > for 'standard', what does standard mean?  What standard?  POSIX?  Or do
  > you mean, by standard, "commonly available"?  If the latter, what common
  > needs are currently unmet by what comes with Tcl ?  I know what that
  > involves for me - but I don't know if that is the same as for you.  Are
  > we talking about taking a set of existing extensions, modifying them
  > until they all 'play nicely' and then shipping one monster sized tar file?
  > Or what?

Must-have: Easy way of using a large collection of packages (maybe even hierarchically named packages.) Perl seems to have some good ideas in this area (I don't know Python) and I see no reason not to learn from their experience. Must be fairly easy to write a package.

Nice-to-have: Way of downloading packages from the 'net. Standardized indexed documentation scheme. Masses of stuff, platform-independent, platform-dependent, even OS-dependent; the more the merrier! A way to combine documentation and code in a single file (like POD) would be good.

  > The faster and more specific we get, the sooner someone can write up TIPs,
  > begin working, etc.

Absolutely.

Donal.


I recommmend that OOP Tcl support the following standard OOP features:

  • Inheritance
  • Interfaces [What are interfaces - the ability to define the contract or API of a class and its methods?]
  • Constructors (Constructors with multiple signature support is preferred)
  • Destructors or some kind of finalize method call when the object is unloaded from memory.
  • Multiple method signature support or method overloading. This is support for methods of the same name but with different input parameters.
  • Method overriding. This is when an inherited class overwrites a method in the superclass. This is known as polymorphism.
  • Finalize support. This adds security to prevent an inherited method from overwriting a superclass variable or member when needed.

Scott Nichols

NEM This sounds suspiciously like java. Interfaces are nice in Java, but I'm not sure of there usefulness in Tcl. For a start, interfaces seem to be a concept that only really applies in typed languages. In Tcl it seems easier to just document interfaces. If an object doesn't supply a particular method, then trying to access it will throw an error which can be dealt with. Interfaces in Java tend to be most useful as a compile-time check to make sure that you've done everything you were supposed to. I don't see how Tcl could do that (having no separate compile phase), and so the interface checking would have to be done (and throw any errors) at runtime -- which is pretty much the situation we currently have.

Having multiple method/constructor signatures seems to be mostly used for passing different sets of arguments. Tcl has a couple of ways of solving this:

  • For methods, you can use arguments with default values: proc foo {arg1 {arg2 default}} { ... }
  • For "constructors", I suppose Tk-style named options solve this problem too: set foo [Foo -option1 $val1 -option2 $val2]

There are probably other methods. "Method signatures" conjures up images of a typing system again, which isn't Tcl.

Finalize support -- I assume you mean the Java "final" keyword here? I always thought this was just to allow compiler optimizations, and had little practical value? RS: We have simple ways of guaranteeing constants - again, only at runtime, of course.

As for inheritance, I've become quite attached to snit recently, which prefers delegation over inheritance. Both seem to be useful in places.

The original quoted posting from Will Duquette stated that we needed two things: an object system, and a standard library. Will has created snit, which is an excellent object system (but maybe not the best object system), and that is now in tcllib - which is an effort to create a standard library. So, I think Tcl isn't quite so far behind other languages, except in perception.

RHS At the last Tcl conference (2004), dkf presented his OO system (oo2) that used a prototype based system. In effect (iiuc), it allows the programmer to use any object as the class definition for another object, and build on it (inheritance). He provides a class based layer on top to make class oriented programmers more comfortable. That seems like the best possible approach for Tcl, imo.

RS has learnt that interfaces are just classes without instance variables (i.e. methods only), from which actual implementations will subclass...

DKF: Commenting on Scott's list...

  • Inheritance means either that you have a class hierarchy, or that you have prototypes (which is something I did in oo2 and which was stolen shamelessly from Self; I think that there's another OO system for Tcl which does prototypes too, but I can't remember which.)
  • Interfaces comprise a structured set of methods which present some kind of API that the instances of the interface implement. By implementing an interface, you're declaring that you participate in the API in the role specified by the interface. (i.e. an interface should also specify some semantics too, even though this is not generally enforcible in Java.)
  • Constructors and destructors are useful; they allow you to make sure that any complex state associated with the object is handled correctly (and state encapsulation is the #1 benefit of objects).
  • Method overloading is difficult in Tcl; command names have typically not been factored along that axis and anyway the language has always supported safe varargs (e.g. the args formal in proc) so the inclination is against this. It could be modelled in the object dispatcher command, of course, but it is not "Tcl-ish" IMO.
  • Method overriding is virtually required anyway if you support inheritance. If you have overriding, you also have to have a mechanism for ensuring that the subclass method can call the superclass's implementation of the method on the object (which can be quite complex to implement).
  • "Final" is a Java mechanism for creating classes which cannot be subclassed, and methods and fields which cannot be overridden. (Assuming that's the right term for fields). It is mainly a security feature which exists to stop untrusted code from getting inside trusted classes. Frankly, I prefer Tcl's security model. :^)

Actually, the original statement from Will, quoted about, says

 "> :For Tcl to catch up, it needs [...] a
 >  :substantial standard library that's pre-installed."
 >  :^^^^^^^^^^                          ^^^^^^^^^^^^^

Tcllib, unfortunately, only meets one of the 3 criteria in this statement - it is considered, by many, to be a standard library. It does not come pre-installed with Tcl (the topic which was at hand) and what is there, while greatly appreciated, has a ways to go before it has the amount of substance that Perl or Python's libraries have. However, that is really a topic for another wiki page.


Some good examples for object system. Not C++ and Java that are really not good example for Tcl.

They all are not typed languages.

[name redacted]: the languages in this list use dynamic instead of static typing, but they are certainly typed languages.

Artur Trzewik I confess it is true. Smalltalkers say: "It is implicit typed". The interpreter tests internally every object call (by doing so called method dispatching). In opposite: "not typed language" is for example assembler. C&C++ programms can access with pointers not proper memory or send procedure calls to nirvana. In this way Tcl or Smalltalk are more "typed" or better "type safe" than C or C++.

I do not expect that "Tcl'ers first days guy" could develop new good OO-System for Tcl. They are absolutly C oriented (or C++ polluted) and most of them say that Tcl does not need OO. (consider theory of paradigm change). On the other side there are enough good external Tcl OO-System (in my opinion the best one is XOTcl) that there should be one that will suffice more than most Tcl'ers can imagine (see above). It is absolutly possible to develop OO in Tcl now, but I would not expect it will be put in Tcl Core. It were also not good.

RS: In a world without walls and fences, who needs windows and gates? In a language which allows to give objects any properties or methods, who needs constraints of classes, interfaces, templates? These are found with compiled, class-oriented languages, with the dichotomy that classes are not "first-class" objects. In dynamic languages like Smalltalk or Tcl, we can easily assume that classes, objects, any value can be referenced and hence similarly treated at runtime - where everything is a string :-)


Information structure versus information semantics!

OOP is usually used to refer to a group of concepts; this somethings causes confusion, since those concepts can be achieve without the association to OOP.

I am mostly interested in the OOP ability to define structures, and create stateful process; currently there is no easy way to do either; Tcl 8.5 will introduce dicts, which will help define structures.

Implementing a common interface to multiple structures is made easier with some of the OOP idioms, but OOP is definitely not a must.

A common interface to multiple structures provides what I like to call polymorphism on the information semantics level.

Tcl currently support polymorphism at the structure level; everything is a string; if it looks like a number it is a number; if it looks like a float it is a float; though everything is a string.

Now stateful process - that's another story, I don't know of any easy way for this to be done; the only way is throught global variables, which should now be nicer with namespaces. But I think it can be made easier.

At the end I have some recommendations.

One, let's not add OOP to Tcl just for the sake of having OOP; let's decompose OOP, discuss its programming benefit, and see what's currently hard or flawed in Tcl, and try to solve it.

Two, let's not reinvente the wheel. OOP can start endless discussions, many of which can be ... I don't know how to say it, too philisophical and too sophisticated for the pragmatic programmer; such discussion are better handled by academics, and I think XOTcl have the strongest academic backing. I, personally, wouldn't swallow an OOP system added by a pragmatic programmer - OOP is really too philosophical for the type. Pragmatic programmers should focus on implementing solutions (Jim), not concepts (OOP), leaving OOP to the academics

So if the community just decided to abandon pragmatism and add an OOP for the sake of OOP, then please select an academic system. I consider Tcl to have its roots in academia, as I consider John Oustershot an academic.

Just to take a shot at it, I think the most flawed thing in Tcl is error handling; many errors in Tcl are handled as normal return values. For example

 lindex {1 2 {}} 3
 lindex {1 2 {}} 2 

I don't like that behavior; I know that Tcl can raise an error, and that the above behavior is mostly an api error, but it's still not acceptable!


RS: Tcl makes it so easy to model the language to one's needs:

proc safe-lindex {list index} {
    if {$i < 0 || $i >= [llength $list]} {error "list overrun with index $index"}
    lindex $list $index
}

SYStems: I don't approve, a solution should be to make lindex call the safe implementation. And since lindex is a command (nota proc, one can redefine procs with the help of the info body and args command), I don't see a simple way to do it. But also since Tcl is interpreter, one can imagine implementing guard that intercept every call to lindex and call the safe implementation instead. As in treating calls to lindex as an event

LV: I guess I'm not certain of SYStems' point - why not just rename lindex to lindex_orig, then change RS's code to define lindex instead of safe-lindex?

SYStems: I just didn't remember the rename command, or didn't know it can do that, I read the docs, and it's suggest using rename for exactly this case, this is intriguing and frustrating at the same time, because regardless if whether or not I knew it exist, I couldn't imagine it, so sad :(

RS: Hey, cheer up - nobody knows everything, but reading man pages is a good way to extend one's knowledge :) But for code that others can easily read, it's better to use original lindex with the original behavior, and safe-lindex where you feel you need it. The more you change the documented API, the less help are man pages for other readers - or yourself...

LV It's the same in most of life - one's world view shapes one's language and one's approach to problem solving. Someone who has no concept of what a computer can do won't, in general, think of solving problems using the computer. One who doesn't know what Tcl does, or the various Tcl extensions, won't think of those commands when forming an approach to solving a particular problem. For some reason, we aren't omniscient.


Thomas Morel: OOP Tcl should support a basic garbage collector, a feature lacking in Tcl OO extensions.

SDW: In true Tcl fashion, there are ways of obtaining the benefits of garbage collection without native garbage collection. In my web apps I have a spawn method for tables that either returns the name of an object for a record, or creates a new object if it doesn't exist. When a record is created, it registers itself with the "thanatos" object, who every minute or so signals record objects to die if they have not been called upon. In my case I have objects that I NEVER want to die, and objects that can pass in and out of existance no fuss no muss.