The issue in the page title is an extended subplot in the history of Tcl development. Among the milestones are the TIPs:
In early 2007, the state of the matter seems to be that acceptance of TIP #257 will make the matter moot (it won't be Itcl, but chances are that noone will mind the difference). What follows below is therefore more of historical interest, but it still seems to be a very thorough analysis.
DKF: Itcl 4.0 will be distributed with Tcl 8.6. This document is thus at least partially obsolete, though some of the ideas it mentions most certainly are not.
A position paper by Mo DeJong. If you have been underground for some time (perhaps waiting for the US elections to end), you might not have heard about the recent talk of adding Itcl to the Tcl core.
I took it upon myself to compile a list of some of the issues that were brought up by this whole "Integrating Itcl" discussion. Note that this paper has not been proof read, so please try to ignore any stupid spelling errors and the like :)
cheers - Mo DeJong - Red Hat Inc
What exactly am I talking about?
Question: What is the best way to integrate Object Oriented functionality into the Tcl core?
Answer: Integrating the OO features provided by Itcl into the Tcl core. Itcl will provide the OO support that programmers desperately need. The trick here will be doing this in a way that maintains the "Tao of Tcl".
For example, there would be no itcl::class command. The class command would become a new global command.
The OO functionality provided by Itcl would become a standard part of the Tcl language, Itcl as a separate entity would cease to exist except for use in backwards compatibility.
OO support in Tcl is a major new feature, it cries out for a new major release number. Existing Itcl users are going to have to update scripts to make best use of this new OO system. Existing Tcl users will run into problems is they define commands like body, class, or the like. Moving to a new major release will give users an obvious indication that they may need to re-examine script during an upgrade.
What exactly does "Integrate Itcl" mean? Well, it depends on who you ask.
- I think that most will conclude that the real solution lies somewhere between these extremes.
- The whole notion of "compatibility" is a double edged sword. While it does help to keep old code working, it also can lead to lack of improvement for fear of effecting existing code.
When it comes to integrating Itcl into Tcl, compatibility with existing or older versions of Itcl is not the overwhelming concern. The solution that has been discussed is to provide a backward compatibility layer that would map to Itcl 3.0 only. This would provide an upgrade path to those folks currently running with Itcl 3.0.
- Some believe quite strongly that to change anything about Itcl is an attack on every Itcl user. One person described any modification to Itcl's syntax or semantics as "spitting in the eye of every Itcl user".
- Others contend that Itcl is so widely used that it simply must be "right". It is described as a "proven technology" so no changes could possibly improve it.
- Still others content that "now is not the time to make changes". The reasons given are an extreme sense of urgency, a belief that any change will be impossible to implement, or that anyone other than the original author is simply unqualified to even suggest changes.
I have asked the question before, but have yet to hear an answer. If not now, when would be a better time to change Itcl? After a larger base of Tcl users have begun to build software on the combined Tcl+OO interface? After new Tcl 9.0 books are published? When exactly?
- If you are unwilling to consider any changes to Itcl, you should stop reading now.
- Improved OO integration can lead to an easier to use system. Far too many people avoid Tcl because they hear oversimplifications like "there is no OO in Tcl". We need to extend the Tcl user base, not worry about the significantly smaller group of existing Itcl users.
- Avoiding "legacy issues" is a serious concern. What seems simple to one person may not be simple to someone else. In five years, nobody is going to care if Itcl defined the variable command before the Tcl core did. They are going to care if confusing incompatibilities exist, and can not be removed because of backward compatibility concerns.
- Itcl contains a number of features and concepts that are fundamentally at odds with Tcl. If we avoid addressing these issues now out of a sense of expediency, we could be making it harder to maintain and improve Tcl in the future.
- Itcl is no different than any other extension. It is not "special". If there are incompatibilities, then they need to be worked out before the code is integrated, not after. We can not subscribe to the theory that some parts of Tcl are "off limits".
- Provide compatibility layer for older code. This is the same approach used by Itcl in previous releases. This time, it would be implemented with a Tcl only package instead of keeping a bunch of old C code around.
- Any change is going to require a bit of rewriting and updating. We should focus on how we can improve the users transition with tools like the Tcl-Pro Checker and Source-Navigator.
- A common theme seems to be:
"Itcl has been stable for 8 years, you can't change it now!"
In reality, there have been major overhauls of the syntax and semantics of Itcl during the 1.X -> 2.X -> 3.X transitions.
Some have contended that these have been "minor improvements" and that they don't matter because backward compatibility was retained along the way. Ironically, this is not much different than what I am suggesting.
- Itcl is just like C++
Itcl is not so similar to C++ that it is immediately intuitive. There are plenty of concepts that just don't translate between the two. Many of them are rooted in the interpreted native of Tcl itself. Some diffs between C++ and Itcl:
- the access keywords are the same as c++ but the defaults access is not.
- That static modifier is not really the same concept as "common". The static modifier in C++ is applied to both methods and data. Itcl generally avoids the concept of a "modifier". Itcl uses a different command for each type of declaration (proc, method, common, variable). There is of course an exception, Itcl uses modifiers for access control (public, private, protected).
- Itcl is bug free!
My own experience seems to contradict the assertion that Itcl is as stable as the Tcl core.
I have used Itcl sparingly in the past, mostly because it was not part of the "core" so you really could not depend in it being installed.
I now hack on Itcl code full time. I have seen many of the legacy issues from the perspective of a user with a large Itcl code-base. I think it is safe to say that Itcl is not as stable as the Tcl core.
For example, a new C++ programmer might try:
itcl::class Toaster { Toaster { name } { puts "Just created a toaster named $name" } method toastme { } { after 5000 "set toast_done 1" vwait toast_done } } Segmentation fault
Clearly, this is not the most effective feedback Itcl could provide.
The point here is not to pick on Itcl. It is clear that much of the code in Itcl is well tested. However, there can be no doubt that there are far fewer Itcl users than there are Tcl users. There can also be no doubt that Itcl has historically had less "support" than the Tcl core. With fewer people working with the code from day to day, nasty corner cases are more likely to go undetected. Only by incorporating the OO functionality provided by Itcl, will the last of these bugs be worked out. I would hope that we can dispense with the notion that there are no bugs in Itcl and move forward.
- classes - instances (objects) - instance methods - instance variables - class wide methods (class procs) - access control for methods - automatic scoping of variables - inheritance and multiple inheritance
Let's ignore the older backwards compatibility commands included in Itcl 3.0 for the sake of brevity here. When we talk of "Integrating Itcl" what we are really talking about is moving the following commands from the itcl namespace and defining them as global Tcl commands.
local class body configbody ensemble find scope code delete
In addition, there are the commands that are only accessible while a class declaration is being evaluated. These commands include:
private protected public variable common method proc constructor destructor inherit
One of the most fundamental questions that needs to be addressed is how objects will be created and destroyed. Having a multitude of ways to create/destroy objects is just plain confusing. We must define the "preferred method" of object allocation and destruction.
Some have suggested that a "new" command should be introduced. This command would function like the new operator in languages like C++ or Java.
% set obj [[new MyClass inst1]
This sort of makes sense when you consider that Itcl provides a delete method. In addition, if an object is only accessible via the new command, one would have less worry of "pollution" of a namespace with lots of class constructor commands.
Itcl currently uses a model where the name of a class is a command that invokes the constructor for a class. This is also the model used by Tk. To be perfectly blunt, it is just too late to change the way Itcl objects are allocated. Even if Itcl was changed, it is far too late to change Tk and object allocation in Itcl and Tk need to be identical.
While object construction is set in stone, the way objects should be destroyed is not so clear. We need a solution that provides a single way to destroy both Itcl and Tk objects.
1: delete instance command
% $obj delete
This was originally implemented in Itcl 1.X, it was later removed. It turns out that this conflicted badly with Tk widgets like Text, the already define a delete command.
2: delete command
% itcl::delete object $obj
This is the approach currently employed by Itcl. It is totally incompatible with the destruction semantics of Tk widgets. This alone is serious enough to warrant the removal of this command.
3: rename command
% rename $obj {}
This approach is also used in the current version of Itcl. This functionality can't be removed because it would break all sorts of Tk scripts. It should be replaced by something better and deprecated.
4: destroy command
% destroy $obj
This approach was used in early versions of Itcl. The problem was that the implementation was not transparent. The destroy command should be moved into Tcl in a way that makes it easy for Tk to simply extend the functionality to allow transparent destruction of Tk widgets. I will avoid the issue of raising/quashing exceptional conditions here.
Incompatibility, duplicated functionality, and bears. Oh my!
In this section we will explore how some Itcl features conflict, overlap, or fit in with features of the Tcl core.
A - Declarations B - Class re-declaration C - Namespaces D - Access control E - Class procs F - Methods G - Option management H - Callbacks and access control I - Working around scope issues J - itcl::find K - The variable command L - Object aggregation
Tcl does not support the concept of declaring the way something should be used before it is used. Variables are set to a scalar, array type, or list type based on use. Procedures are defined at runtime, and are not declared. Tcl has no function prototypes. Itcl adds the concept of classes that declare what functions they support. One can define commands inside a class definition (much like Java), but it is often better to split the declaration and the implementation of class methods up. This helps with readability and makes tracking down syntax errors easier.
Itcl requires that you declare instance commands (methods) and class wide commands (class procs) differently, but it has no support for declaring the type of a variable at class definition time.
For instance:
itcl::class Foo { variable foo }
But not:
itcl::class Foo { array variable foo }
A class can not be redefined. This directly contradicts the semantics of variable and proc definitions. Some have suggested that .h files are the best way to avoid any problems this causes. I frankly don't see a need for the added complexity. Redefining a class should be the same as calling "itcl::delete class Foo" and then running the class command again.
The argument has been made that this actually helps programmers in the case where the same class name is accidently used in two completely different files or modules. Tcl has never provided this sort of "help". In Tcl, the same proc or variable can be created, deleted, or redefined in any file without having to worry that it had been defined elsewhere.
The most common way one would run into this problem would be to source a file that contained a class declaration twice. The second time you run [source foo.tcl] an error would be raised by the class command. There is of course a trivial optimization one could employ here. If a new class declaration is exactly the same as the old one, simply do nothing.
Itcl makes use of Tcl namespaces, but it also adds additional features on top on the existing namespace support provided by Tcl. There is a long history of disagreement over the issue of access control and how it relates to namespaces.
- Access control was not provided in Tcl 8.0 - Ancestor based cmd/var lookup was not provided in Tcl 8.0
In Itcl 3.0, the ancestor based cmd/var lookup features support was dropped. Access control on the other hand, was retained.
The differences between namespaces and Itcl classes is the cause of much confusion. I have also heard the opinion expressed that as soon as people have access to classes, they will no longer need to worry about namespaces.
This "solution" to the problem of incompatibilities between Itcl and Tcl is not much help. Either Tcl namespaces need to be changed so that they provide functionality that Itcl requires or Itcl will need to be modified to require only the functionality that Tcl namespace provide. If people are having trouble seeing how namespaces and classes fit together, perhaps it is because they do not. We need to address the issue and not gloss over it with "quick fixes".
Tcl provides no support for access control. Both variables and procedures can be freely modified by any part of a Tcl program. There tend to be a couple reasons to put access controls on variables and procedures. One reason is to stop programmers from accidently accessing a method or data. If two different modules accidently used the same global variable, the result could be a disaster. Access control also plays a documentation role. If a variable or method is declared "private", that would provide a very good hint that the author did not want you to use that function from outside the given class. Not being able to call a private method from the public instance handle is a good clue that you not meant to use it. Itcl's access control policy does make it harder to get access to private data, but it does not actually stop anyone from reading or writing variables or calling private methods from outside of the class scope.
Access control is currently in a "half and half" state. Tcl namespaces do not implement any sort of access control. Itcl tries to layer its own notions of access control on top of Tcl namespaces, which causes some problems.
itcl::class DontCallMe { private proc argh {} { puts $msg } private common msg "ARRRAGH!" } % namespace eval DontCallMe {argh} ARRRAGH! % set DontCallMe::msg "WOHOOO!!" % namespace eval DontCallMe {argh} WOHOOO!!
Note how in the above example, we can directly set a private class variable and we can call a private method by simply adding a namespace eval. In fact, this is exactly what the itcl::code method does.
Let's face it, control is complex! Misuse of access control can easily lead to some really tricky bugs.
In this example we see how Itcl's existing access control features can be misused in a way that breaks perfectly good code. One can redefine subclass method access in a way that break the API of the parent class with respect to an isa test.
itcl::class Base { public method doit {} { return XJ45.1 } } proc print_base { base } { if {[[$base isa Base]} { puts "!!!!! [[$base doit] !!!!" } else { puts "not derived from Base" } } % Base b % print_base b !!!!! XJ45.1 !!!!
And now for a subclass:
itcl::class Derived { inherit Base private method doit {} { return XJ45.2 } } % Derived d % print_base d bad option "doit": should be one of... d cget -option d configure ?-option? ?value -option value...? d isa className
This example shows how one can break the isa relationship by misusing access controls.
These sorts of procs are defined with the itcl::body command. They are subtly different than regular Tcl procs. For instance, in a class proc variables defined as common are accessible by default. A regular Tcl proc only has access to local variables by default. Procs defined with the body command also make use of access control, regular Tcl procs have no access protection.
At first glance, these two bits of code might seem like they do the same things:
namespace eval Two { proc gak { } { return GAK2 } } % Two::gak GAK1 itcl::class One { proc gak { } { return GAK1 } } % One::gak GAK2
But, when you introduce a variable into the mix, the difference shows.
namespace eval Two { variable result GAK1 proc gak { } { return $result } } % Two::gak can't read "result": no such variable itcl::class One { common result GAK1 proc gak { } { return $result } } % One::gak GAK2
One needs to explicitly make a namespace variable visible to a regular Tcl proc defined in a namespace.
namespace eval Two { variable result GAK2 proc gak { } { variable result return $result } } % Two::gak GAK2
The new "class proc" type also makes it impossible to declare a regular proc in a class definition. One would need to surround a proc definition with a [namespace eval] to get a similar effect.
The whole concept of a "class proc" is in conflict with the behavior and method of declaration of regular Tcl procs.
There is also reason to be concerned about the way itcl::body duplicates functionality seemingly covered by the proc command. Tcl has always had one way of declaring a proc, with the proc command. We need to think long and hard about our options before changing that.
Instance methods are the second type of new command added by Itcl. Methods are different than regular procs, since they can only be invoked on a valid object instance.
Here is a quick example of how they are currently defined using itcl::body.
itcl::class Example { private proc foo {} private method bar {} } itcl::body Example::foo {} { puts "called foo" } itcl::body Example::bar {} { puts "called" }
Methods are not really incompatible with anything in Tcl, but the way they are defined is.
Tcl has always used the proc command for this sort of thing. We need to be absolutely sure that adding a toplevel "body" command to Tcl is really the optimal solution. I am not convinced that it is.
Itcl's concept of configurable options is tightly coupled with "public variables". These public variables can be declared only when the class is created. This approach works well for simple cases, but it fails to meet the needs of more complex applications.
Evidence of this can be found in itk, the mega-widget based framework build on top of Itcl. Instead of using itcl style public variables, Itk stores object configuration options in an array named itk_option.
This make it possible to add an option inside an object's constructor, something that you simply can't do in Itcl. The fact that Itk uses its own option configuration system can lead to some serious problems when mixing Itcl and Itk based objects.
Here is a quick example:
itcl::class Foo { inherit itk::Widget public variable myoption off } % Foo .f % .f cget -myoption unknown option "-myoption"
Confused? You should be.
This error is a side effect of itk's implementation. Itk replaces Itcl's public variable based option configuration, and rightly so. An option system that is statically defined at class declaration time is at odds with the dynamic needs of mega-widget systems.
Options also have their own special sort of callbacks, and a special way to define these callbacks.
The itcl::configbody command supports a sort of inlined trace method that in run inside the scope of an instance when an option is configured. When a user runs
% $obj configure -foo bar
a configbody callback for the -foo option is called. The configbody is a bit of a strange mix between a variable trace and an instance method.
It can be handy, but it also adds an additionally layer of functionality that could be implemented without the need to introduce a new global "configbody" command.
The configbody command also has a major shortcoming. The configbody command is like a write trace, but there is no support for a read trace on an option. This is a serious problem, you can work around it but it is not easy or fun.
It is just not clear that the functionality provided by the configbody command is important enough to require the addition of a global Tcl command for it.
The itcl::code command provides a way to bypass access control for Itcl procs and methods.
Example:
set cmd "" itcl::class Example { constructor {} { global cmd set cmd [[itcl::code $this foo] } private method foo {} { puts "called foo for $this" } } % Example e1 e1 # Run the callback saved in $cmd % eval $cmd called foo for ::e1
The itcl::code command works by creating a "backdoor" into the Example namespace, like so:
% set cmd namespace inscope ::Example {::e1 foo}
Of course, the constructor for the Example class could have been coded without the itcl::code method, but it would have been slightly uglier:
constructor {} { global cmd set cmd [[namespace inscope \ [[namespace current] [[list $this foo]] }
The itcl::scope command provides a "backdoor" for variable scope. There is no access control for variables. You can always set a variable as long as you know the full scope of the variable name. The itcl::scope command provides a handy way to get this info.
The ability to access instance variables using the scope command was added for Itcl 3.0. This is an incredibly useful feature, especially for Tk GUIs. Older versions of Itcl required the same global array indexing hacks that are blamed for the poor performance and readability of tcllib.
A quick example:
set var "" itcl::class Example { constructor {} { global var set var [[itcl::scope _name] } private variable _name "bob" }
Note that unlike the itcl::code command, the itcl::scope command does not require that you pass the name of instance variable $this.
% Example e1 e1 % set [[set var] bob
This works because itcl::scope lets us bypass any access protection and directly access the instance var.
Here is how it is implemented:
% set var @itcl ::e1 ::Example::_name
The above list may not look like other Tcl variable names, it is not. It is a special form of a variable name known only to Itcl. Itcl uses a C API to register a custom variable name resolver when it is loaded into Tcl.
The issue with scoping is not so much an incompatibility with Tcl as much as it is a new feature that the core may need to support directly. One possible modification would be to use a separator other than space in the variable name. There will be code that assumes variable names do not have spaces in them, for instance when a var name is passed to an eval. I assume that a toplevel scope command would be created, but there are certainly other options if avoiding namespace pollution is a big issue.
The itcl::find command was originally incorporated into the info command. It should be put back. There is no justification for adding a toplevel find command just to support searching for objects.
It might also be useful to look into adding a way to determine if an object exists without having to compare to a named object result. This would make the users life easier and would surely execute more quickly than the current solution. It would be like the winfo exists command, but for objects.
Itcl's variable command is in conflict with Tcl's variable command. In fact, they are almost exact opposites.
Here is a quick example.
namespace eval One { variable Number 1 } % set One::Number 1 itcl::class Two { variable Number 2 } % set Two::Number can't read "Two::Number": no such variable
Why ,you ask?
Itcl uses the variable command to define an instance variable. Tcl uses the variable command to declare a namespace variable, similar to a common variable in Itcl.
like so:
itcl::class Two { common Number 2 } % set Two::Number 2
I personally find the common command a bit confusing. It does not make it particularly obvious that the thing being declared is a variable. When you combine common with and inlined variable initialization and an inlined configbody callback, the result can end up looking a lot like a method declaration.
itcl::class Two { common Number {one two} { puts "called me" } }
Itcl has no built-in support for object aggregation! It is really quite baffling. Object aggregation is an incredibly useful concept and it is well supported in languages like C++ and Java.
Here is a quick example: (C++)
class Member1 {}; class Member2 {}; class Aggregate { Member1 mem1; Member2 mem2; }; (Java) class Member1 {} class Member2 {} class Aggregate { Member1 mem1 = new Member1(); Member2 mem2 = new Member2(); }
To do this same thing in Itcl, the programmer needs to allocate and free the objects by hand.
itcl::class Member1 {} itcl::class Member2 {} itcl::class Aggregate { variable mem1 variable mem2 constructor { } { set mem1 [[Member1 ${this}_mem1] set mem2 [[Member2 ${this}_mem2] } destructor { itcl::delete object $mem1 $mem2 } }
Now for the fun stuff! In this section I present some suggested changes to better integrate Itcl into the Tcl core. Each layer suggests more comprehensive changes than the previous one, and some changes build upon the previous layer.
These suggestions assume that backwards compatibility would be dealt with using a loadable package that would wrap around the new implementation.
No changes!
This approach would simply move all the Itcl commands into the global namespace. This is an all or nothing approach. It is not going to be possible to move some Itcl commands into the global namespace and leave some in the itcl namespace. That would only serve to break the scripts of folks that run [namespace import itcl::*]. Still, not everyone is going to be happy. There will be folks that use the itcl:: prefix for method invocations, those scripts will be broken by this approach.
The "no changes" approach would most benefit those who already have large applications written in Itcl. The benefit being they would not need to rewrite an application that was written to the Itcl 3.0 API.
If the application used any older 1.X or 2.X APIs that are still supported in Itcl 3.0, it would not work with the new integrate OO system. Solving that legacy code problem would mean introducing even more toplevel command like itcl_class and so on. It is a slippery slope, we need to cleanly integrate Itcl 3.X, and use add on packages for backwards compatibility. It seems obvious that the best people to implement such a wrapper would be those folks with a vested interest in getting legacy code running on the OO enabled Tcl core.
Some folks contend that Layer 0 is the only valid option. They claim that any syntactic or semantic change will alienate current Itcl users. While this is a concern, it is not predominant.
Changes that are not likely to cause incompatibilities and will fix real problems or simply remove obsolete code.
1.1 Constructors must return fully qualified names:
1.2 Remove class autoloading
1.3 Disallow dynamic method invocation in constructor
The problem is described here:
http://dev.scriptics.com/lists/itcl/2000/11/msg00031.html (Link is dead. No replacement found yet)
The solution is to simply not allow it.
1.4 Disallow dangerous method protection changes
The problem is described in incompatibilities section D. Changes that break an object's isa relationship should not be allowed.
1.5 Allow redefinition of classes
The class command should not raise an error when duplicate class is encountered. If the class declaration has changed, the system should call [itcl::delete class $class] and then redefine it. In the trivial case where the exact same declaration body is twice, the second declaration can simply be ignored. We should not force Tcl programmers into a C/C++ style model where .h file are the norm.
Incorporation of Itcl functionality into existing Tcl commands.
2.1 proc command can replace the body command
This will address incompatibilities discussed in sections E and F. The existing class declaration syntax would still be used, but the proc command would be substituted for the itcl::body command.
class Example { proc foo {} method bar {} } proc Example::foo {} { puts "called foo" } proc Example::bar {} { puts "called bar" }
This approach would mean that the itcl::body command would not longer be needed.
2.2 [info objects] instead of itcl::find
This was discussed in section J, itcl::find is simply not useful enough to deserve its own global command. The functionality of find should be incorporated into the info command.
Changes to the object declaration syntax (but not semantics) of classes.
In this section, we explore the impact of adding an "instance" modifier to the class declaration syntax. This addresses two issues. The first the variable command, as described in section K. The second is an objection to using proc instead of body, as described in Layer 2. Some folks felt that using the proc command to define the body of a "method" could be confusing.
3.1 instance modifier
For variables:
class Example { instance variable _name "bob" variable _total 0 }
Instead of:
itcl::class Example { variable _name "bob" common _total 0 }
For methods:
class Example { proc foo {} instance proc bar {} } proc Example::foo {} { puts "called foo" } proc Example::bar {} { puts "called bar" }
Instead of:
itcl::class Example { proc foo {} method bar {} } itcl::body Example::foo {} { puts "called foo" } itcl::body Example::bar {} { puts "called bar" }
This approach would solve the problem of incompatible variable commands. Using an "instance" modifier would mean that "common" and "method" would no longer be needed. That is a good thing in my book, "instance variable" is a lot more descriptive than "common".
Semantic changes that would require some rewriting of effected code.
4.1 Get rid of class procs
Many of the problems mapping Itcl classes to Tcl namespaces seem to be related to class procs. Class procs are also cited as a reason that the proc command can not be used instead of the body command.
When you get right down to it, class procs are simply incompatible with Tcl procs. They have different variable access defaults and class procs employ access control. Why should there be a distinction between procs defined in a namespace vs procs defined in a class body?
I suggest the removal of the entire concept of class procs. Programmers would need to explicitly import namespace variables and there would be no access control, but Tcl programmers have been dealing with these issues for a long time. The benefit would be a significant simplification of the implementation, since folks would be using regular Tcl procs.
Large changes that would modify both the semantics and syntax of object use. These changes would not be backward compatible by any stretch of the imagination.
5.1 Don't use public variables as options!
The use of public variables as the means to specify object options was a mistake. Itk's custom option system is proof that Itcl declaration based option system does not scale well. There is no reason to re-invent the wheel here, we can simply add itk's option interface directly to Tcl. This solves many of the issues described in section G.
The first problem, is of course the command and variable names used by itk. One possible approach would be to use "oo_option" instead of "itk_option".
class HiThere { oo_option define -command {puts "hi"} constructor { args } { oo_option initialize $args puts "-command is $oo_option(-command)" } }
This approach to option management would also remove the need for a itcl::body command. A configbody callback is really like a variable trace, why don't we leverage that? It would also be trivial to add support for read traces.
class HiThere { oo_option define -command {puts "hi"} oo_option trace w -command { puts "wrote -command value is \"$oo_option(-command)\"" } oo_option trace r -command { puts "read -command value" return $oo_option(-command) } constructor { args } { oo_option initialize $args } }
Clearly, a number of implementation details would need to be worked out. The general idea is the important thing here.
5.2 Object aggregation support
Section L outlined the need for object aggregation support. The question remains, how do we implement it? Why don't we use the "component" interface defined by itk. The command and variable named would need to be changed. Perhaps oo_component would work.
Here is the example from section L, re-implemented using an oo_component style interface.
class Member1 {} class Member2 {} class Aggregate { constructor { } { oo_component add mem1 { Member1 ${this}_mem1 } oo_component add mem2 { Member2 ${this}_mem2 } } }
Note that component objects would be destroyed automatically when the containing object was destroyed.
5.3 Add access control to Tcl
Incompatibility section D outlined many of the problems mapping Itcl's concept of access control to Tcl. The current "half and half" state of access control is a very bad thing, one possible solution would be to tightly integrate Itcl's access control rules into Tcl itself.
This would be a major overhaul, but it seems like it should be possible to do this in a backwards compatible way. The problem with this approach is that it makes Tcl much more complex. It would be hard to argue that "Itcl integration" is not a major undertaking if fundamental concepts like variable access and procedure invocation need to be re-implemented to support Itcl.
5.4 Remove access control entirely.
The other solution to the problems defined in section D is to simply yank all access protection from the OO system. This is not nearly as drastic as it first sounds.
Fear that "out of control programmers" mucking about in internal variables will cause software faults are not founded in reality. One can already access any Itcl variable with the help of the itcl::scope command.
Removal of access control for method invocations would mean that the itcl::code method would no longer be needed. You don't need to use the itcl::code method to subvert access control if it does not exist.
Note that I am only talking about removing access control restrictions for methods invocations. I think the public, protected, and private modifiers should be retained. They provide good documentation as to the programmers intent. Variables would continue to make use of these modifiers to control which variables are automatically made accessible to an instance method.
If unintended method invocation was a problem, a simple naming convention be used. Tcl programmers have been using naming conventions to avoid command clashes for a very long time.
One serious problem would be the possibility of breaking existing code because of newly ambiguous method invocations.
itcl::class C1 { public method foo {} {} } itcl::class C2 { private method foo {} {} } itcl::class C3 { inherit C1 C2 } C3 c3 c3 foo
In this case, the call to foo would be ambiguous if there were no access controls. One could argue that the public/protected/private keywords could be used to disambiguate this method invocation, but I am not sure how convincing that would be.
Removing access control for method invocation would be a big step, but it would significantly simplify the implementation of OO in Tcl.
Assuming each layer of change was implemented, the newly integrated Tcl+OO system would add the following global commands to Tcl.
class destroy local scope
The following commands would defined only within a class body.
constructor destructor inherit instance oo_option oo_component proc private protected public
That's it, let the flaming commence!
Chang Li: Agree 99%.
EMJ: NO - I'm not going to argue with any of the detail, it's the principle I don't like. The principle is called Feature Bloat, and that is not in the spirit of TCL. How many people are already using old versions of TCL, not for compatibility or out of perversity, but because they are small enough?
If you want a feature in TCL, there is, or will be, and extension for it. Extensions are good, just pick the ones you need. If the users might not have them, build a starkit for them.
If something is going in the core, it should be:
LV that's why the TIP that was approved instead proposes including itcl as just another extension. The problem is that to do either will require effort on the part of someone to make the code build appropriately. There was a posting on comp.lang.tcl by Donal in the past few months about what the effort would be to finish up the itcl TIP.
Even with all the noise about how desperate programmers need OO, there still doesn't appear to be even one of these desperate people willing to work through the issues and get this done.
SRIV I've been using snit to create tcl types and tk widgets, not because I want an OO programming system, but because tcl/tk makes it too damn hard to create your own widgets! Maybe it was snit's easy to understand docs that enabled me. Regardless, until using snit, I never used any OO featured language. But, now I see a real need for it. I never tried Itcl, but if it can painlessly do all of what snit can do, and enable me to easily extend tcl/tk with new or compound widgets, I think it would be a worth while addition. Until then, "package require snit" .
DKF: At least some people claim that Snit makes it easier to create new widgets than Itcl.
RS How is it hard to create "your own widgets"? I assume you mean "megawidgets" composed of stock widgets ("in contrast to Megahertz and Megabytes, a megawidget isn't worth a million widgets"...) Whenever I need some custom composition of widgets, I just code
proc myMega {w args} { frame $w thisComponent $w.1 ... thatComponent $w.2 ... ... eval pack [winfo children $w] ;# or grid, or whatever set w ;# so the caller can reuse it }
I think this simple approach beats worrying over complex frameworks, their documentation, their deployment, etc. But of course I also use BWidget at times :)
SRIV yes, for simple cases this is fine. In my case, Im writing a tk GUI builder, that needs to query widgets for their options, handle methods, instances etc. In other words, I needed the megawidget to behave like a real widget in every way. Snit enabled that while simplifying every aspect of working with the megawidgets. In short order, I was cranking out widgets one after another, all with excellent results. For a novice widget builder like myself, snit made megawidget creation posible with simplicity, clarity and speed. A facility that has long been lacking in the core.
LV I think that Sriv is talking about features that make a megawidget a megawidget rather than a proc that creates a complex widget.
The ability of the resulting creation to respond to introspection, to be able to be trivially configured, to interact with the option database, all result in a cleaner, easier to reuse widget.
See also itcl and TIP #257: Object Orientation for Tcl.