-------------------------------------------------------------------------
ABSTRACT
==========
This TIP proposes adding OO support to the Tcl core, semantically based
on XOTcl. The commands it defines will be in the *::oo* namespace,
which is not used by any current mainstream OO system, and it will be
designed specifically to allow classic XOTcl to be built on top.
RATIONALE AND BASIC REQUIREMENTS
==================================
Tcl has a long history of being comparatively agnostic about
object-oriented programming, not favouring one OO system over another
while promoting a wealth of OO extensions such as [incr
Tcl][<URL:http://incrtcl.sourceforge.net/itcl/>],
OTcl[<URL:http://bmrc.berkeley.edu/research/cmt/cmtdoc/otcl/>],
XOTcl[<URL:http://media.wu-wien.ac.at/>],
stooop[<URL:http://jfontain.free.fr/stooop.html>],
Snit[<URL:http://www.wjduquette.com/snit/>], etc. because in general,
one size fits nobody.
However, many application domains require OO systems and having a
common such base system will help prevent application and library
authors from reinventing the wheel each time through because they
cannot rely on an OO framework being present with each and every Tcl
installation. For example, the http package supplied with Tcl has its
own internal object model, and a similar mechanism is reinvented
multiple times within tcllib. Other parts of tcllib do their own thing
(to say nothing of the fact that both stooop and Snit are in tcllib
themselves). This does not promote efficient reuse of each others code,
and ensures that each of these packages has a /poor/ object system. The
request for an OO system is also one of the biggest feature requests
for Tcl, and would make it far easier to implement megawidgets. It also
leaves Tcl open to the ill-informed criticism that it doesn't support
OO, despite being spoilt for choice in reality through the extensions
listed above.
Given all this, the time has come for the core to provide OO support.
The aim of the core OO system shall be that it is simple to get started
with, flexible so that it can take you a long way, fast (we all know
that we're going to get compared on this front!), and suitable for use
as a foundation of many other things, including the re-implementation
of various existing OO extensions, including those that are currently
compiled and also those that are pure Tcl extensions.
Another requirement is that programmers should not have to alter all of
their existing code in order to get started with the new system;
rather, they should be able to adopt it progressively, over time,
because it supports betters ways of working (e.g., faster and more
flexible libraries).
THE FOUNDATIONAL OO SYSTEM
============================
This TIP proposes that the foundation of the OO system should be based
on XOTcl as that is fast, semantically rich, well-supported, and
relatively compatible with with the existing Tcl build system.
Some changes will be necessary. Certain aspects of XOTcl syntax are
peculiar from a conventional OO point-of-view, and it is deeply
unfortunate that a very large number of methods are predefined in the
XOTcl base class. XOTcl's approach to object creation options is also
highly idiosyncratic, and doesn't support the typical Tcl idioms.
However, the changes must be made in such that classic XOTcl can be
built on the new framework; as a result, the classic XOTcl base class
will be derived from something more fundamental.
KEY FEATURES
--------------
* Class-based object system. This is what most programmers expect
from OO, and it is very useful for many tasks.
* Allows per-object customization and dynamic redefinition of
classes.
* Supports advanced OO features, such as:
meta-classes: These are subclasses of *class*, which permit more
advanced customization of class behaviour.
filters: These are constraints (implemented in Tcl code,
naturally) on whether a method may be called.
mixins: These allow functionality to be brought into an object
from other objects if necessary, enabling better
separation of concerns.
invariants: These ensure that assumptions about the behaviour of
a class can be checked.
KEY ALTERATIONS
-----------------
* Object and class names in the core extension to be all
lower-case, in line with best common practice.
* Methods have to be capable of being non-exported, by which we
mean that they are not (simply) callable from contexts outside
the object.
* The majority of the API for updating an object or class's
definition is to be moved to a separate utility command,
*oo::define*.
* More "conventional" naming of operations is to be used.
Note that this TIP does /not/ propose to actually include any XOTcl (or
Itcl or Snit or ...) compatability packages in the core; it it about
forming a foundation on which they can be built (which happens to also
be a comparatively lightweight OO system in itself). Such compatability
packages can either remain separate code, or be the subject of future
TIPs.
DETAILED PROPOSAL
===================
ESSENTIAL CHANGES RELATIVE TO XOTCL
-------------------------------------
This section describes the essential changes to XOTcl behavior required
to meet the above goals, and the rationale for them. The paragraphs
which describe the specific changes begin with the word *Therefore*, in
bold type. Note that whereever possible, the /semantics/ of XOTcl are
to be used even where the syntax is not; deviations will be explicitly
listed.
EXPORTED VS. NON-EXPORTED METHODS
In XOTcl, every class and every object has an associated namespace. The
namespace associated with a class /::myclass/ is
/::xotcl::classes::myclass/; the namespace associated with object
/::myobject/ is simply /::myobject/. XOTcl "instprocs" are simply procs
defined in a class (or superclass) namespace; XOTcl per-object "procs"
are simply procs defined in an object's namespace. /Every such proc
becomes an object subcommand./
This is part of the reason why XOTcl objects have such cluttered
interfaces. Every method which is of use to the object appears in the
object's interface - and there's no way to prevent this.
*Therefore*, in the new oo system "*proc*s" and "*instproc*s" can be
exported or non-exported. Exported procs appear as object subcommands;
non-exported procs do not, but remain available as subcommands of the
*my* command. In this way, the object itself can still use them, but
they need appear in the object's interface only if desired.
Additionally, the standard *info* method will need to be extended to
allow introspection of which methods are exported and which are not.
THE OO::DEFINE COMMAND
In XOTcl, the commands to define per-class methods, filters, and so on
are subcommands of the class object; the commands to define per-object
methods, filters, and so on are subcommands of the individual object.
This is a problem, as it confuses the implementation-time interface
with the run-time interface. The design is logical, given XOTcl's
extreme dynamism; any implementation-time activity, such as defining a
method or adding a filter can indeed be done at run-time. But again,
this makes it difficult to define clean run-time interfaces for
reuseable library code.
The solution described in the previous section, of making some methods
private by declaring them non-exported, does not give us a full
solution; having the *instproc* subcommand available only from instance
code isn't all that useful.
Therefore, we add a new command, *oo::define*, which is used to define
methods, filters, and so on. It can be called in two ways. The first is
as follows:
*oo::define* /objectOrClass subcommand args.../
For example, the following XOTcl code defines a class with two methods:
xotcl::Class myclass
myclass instproc dothis {args} { # body }
myclass instproc dothat {args} { # body }
In the new oo core, the matching code would be this:
oo::class myclass
oo::define myclass instproc dothis {args} { # body }
oo::define myclass instproc dothat {args} { # body }
*oo::define* can also be called with a script whose commands are
aliased to the subcommands of *oo::define*. Thus, the above code could
also be written as follows:
oo::class myclass
oo::define myclass {
instproc dothis {args} { # body }
instproc dothat {args} { # body }
}
Finally, the class "/create/" method could be extended so that it could
be called with such a script:
oo::class myclass {
instproc dothis {args} { # body }
instproc dothat {args} { # body }
}
This allows a class to be defined cleanly and concisely, while
guaranteeing that all class details can still be modified later on
using *oo::define*.
Note that we do not lose any object-oriented flexibility by this
scheme. An /oo::xotcl/ package can use the "forward" feature to forward
"/instproc/" and its partners to *oo::define*, thus defining them all
as methods; and once they are methods, all of the usual techniques of
method chaining, mix-ins, and filters apply.
*oo::define* will need two subcommands XOTcl doesn't currently provide:
*export* and *unexport*. *export* takes as arguments a list of method
names; all named methods are exported and become visible in the object
or class's interface. *unexport* does the opposite. Each can include
wildcards in its argument list, just as *namespace export* does.
STANDARD METACLASSES
XOTcl defines two standard Metaclasses, /xotcl::Object/ and
/xotcl::Class/. /xotcl::Object/ is the root of the class hierarchy; all
XOTcl classes implicitly inherit from /xotcl::Object/. XOTcl classes
are themselves objects, and are instances of /xotcl::Class/.
/xotcl::Class/ can itself be subclassed to produce different families
of classes with different standard behaviours.
The new core object system will use the same basic mechanism, based on
the metaclasses *oo::object* and *oo::class*. However, one of the
problems with XOTcl is that XOTcl objects have too much standard
behavior; the new core object system must provide a simpler foundation,
with the XOTcl behavior optionally available.
*Therefore*, we will decompose the features of /xotcl::Object/ and
/xotcl::Class/ into a number of simpler metaclasses.
*oo::object* will be the root of the class hierarchy. However,
instances of *oo::object* will have a minimal set of standard methods,
so that clean interfaces can be built on top of it, as can be done with
Snit types and instances.
Core object system classes will be instances of *oo::class* or its
subclasses. Likewise, *oo::class* will define only minimal behaviour.
The standard XOTcl class and object methods will be provided by a
number of standard classes, all of which will be subclasses of
*oo::object*. A user-defined class can include some or all of the
standard XOTcl behavior by multiply inheriting from some or all of
these standard classes. Each such standard class will provide a subset
of the standard XOTcl methods. The following is an incomplete list of
the necessary classes:
* *oo::definer* will define one method for each subcommand of
/oo::define/; the methods will be "forward"ed to *oo::define*.
* *oo::struct* will define all of the data access methods, e.g.,
*set*, *unset*, *lappend*, *incr*, and so forth.
Thus, *oo::class* is the mechanism for defining classes with clean
interfaces and maximum data hiding and encapsulation; *oo::struct* is
the mechanism for defining classes for maximal public access.
The above classes and metaclasses will be implemented such that they
can be used as a foundation for the *::xotcl::Class* and
*::xotcl::Object* metaclasses (see below for a discussion).
INHERITANCE
A class may wish to make use of the capabilities of *oo::struct*
internally without exporting its methods.
*Therefore*, the inheritance mechanism should be extended such that the
newly defined class can declare whether a parent class's methods should
be exported or not.
OBJECT CREATION
XOTcl has a unique creation syntax. The object name can be followed by
what look like Tk or Snit options - but aren't. Instead, any token in
the argument list that begins with a hyphen is assumed to be the name
of one of the object's methods; it must be followed by the method's own
arguments. For example, a standard XOTcl class will have a "set"
method, which has the same syntax as the standard Tcl "set" command.
Thus, the following code:
myclass myobj -set a 1 -set b 2
creates an instance of "myclass" called "myobj" whose instance
variables "a" and "b" and set to 1 and 2 respectively. This is an
intriguing and innovative interface, and it is unlike any other Tcl
object system. Additionally, it makes it difficult to implement
standard Tk-like options.
*Therefore*, standard core object system classes will not use this
mechanism (though it might be available on demand by inheriting from
some other standard metaclass). Instead, standard core object system
classes will have no creation behavior other than that implemented by
their designers in their constructors.
Constructors may have any argument list the user pleases, including
default arguments, the "args" argument (as in the *proc* command), and
XOTcl-style non-positional arguments. It is up to the developer to
handle the arguments appropriately.
It is expected that one of the key responsibilities of any XOTcl
compatability package would be to define a constructor that parses the
arguments in the expected way and uses them to invoke methods on the
newly created object.
CONSTRUCTOR SYNTAX
In XOTcl, a class's constructor is implemented using its "init"
instproc. This is troubling; constructors are intended to do things
just once, and are often written to take advantage of that, whereas an
"init" instproc can theoretically be called at any time. For any given
class, then, one of two conditions will obtain: either "init" must be
written so that it can be called at any time, or the class will have an
inherent logic bug.
*Therefore*, the class constructor will not be implemented as a
standard instproc. Instead, the *oo::define* command will have a new
subcommand, *constructor*, which will be used as follows:
oo::define myclass constructor {} {
# body
}
The constructor so defined will act almost exactly like an instproc; it
may have pre- and post-conditions attached to it, it may call
superclass constructors using the "super" command, etc. However, it may
never be called explicitly, but only via the class's "create" and "new"
methods.
DESTRUCTOR SYNTAX
In XOTcl, a class's destructor is defined by overriding the the
"destroy" instproc. This is problematic for two reasons: first, a
destructor doesn't need an argument list, and has no need of
preconditions and postconditions. An instproc is too powerful for the
task. Second, successful destruction should not depend on the
destructor's chaining to its superclass destructors properly.
*Therefore*, the class destructor will be defined by a new subcommand
of *oo::define*, *destructor*, as follows:
oo::define myclass destructor {
# Body
}
The destructor has no argument list, nor does it have any preconditions
or postconditions.
The destructor cannot be called explicitly. Instead, the destructors
are invoked in the proper order by the standard *destroy* method
(defined in *oo::object*), which need never be overridden.
DESIRABLE CHANGES
-------------------
The changes described in this section are not absolutely essential to
meeting the goals described earlier. However, they are desirable in
that they lead to cleaner, more maintainable code.
CLASS VS. OBJECT METHOD NAMING
XOTcl has many features which can be applied to a class for use by all
class instances, or to a single object. For example, a "filter" can be
defined for a single object, while an "instfilter" can be defined for a
class and applied to all instances of that class.
This is exactly backward. Most behavior will be defined for classes;
additional per-object behavior is the special case, and consequently
should have the less convenient name.
*Therefore*, all XOTcl subcommands that begin with "inst" will lose
their "inst" prefix; the matching per-object subcommands will gain a
"self." prefix, to indicate that it is operating on the object itself
and not the members of the class. Thus, a filter is defined on a class
for its instances using the "filter" subcommand; a filter is defined on
a particular object using the "self.filter" subcommand.
PROCS VS. METHODS
The word "proc" conveys a standalone function; an object's subcommands
are more typically described as its "methods".
*Therefore*, the XOTcl "instproc" and "proc" subcommands should be
renamed as "instmethod" and "method" - or, if the new naming convention
described in the previous section is adopted, *method* and
*self.method*.
PUBLIC NAMES
In XOTcl, the main objects are /xotcl::Class/ and /xotcl::Object/.
However, the Tcl Style Guide dictates that public command names begin
with a lower-case letter.
Therefore, all public names in the /oo::/ namespace will begin with a
lower case letter, e.g., the standard core object system equivalents of
/xotcl::Class/ and /xotcl::Object/ will be *oo::class* and
*oo::object*.
The names in any /oo::xotcl/ compatibility module would naturally
follow the existing XOTcl conventions.
API SPECIFICATION
===================
This section documents the core object system API in detail, based on
the essential and desirable changes discussed in the previous sections.
HELPER COMMANDS
-----------------
The namespace(s) that define the following three commands are not
defined in this specification; all that is defined is that they will be
on the object's *namespace path* during the execution of any method and
should always be used without qualification.
MY
The *my* command allows methods of the current object to be called
during the execution of a method, just as if they were invoked using
the object's command. Unlike the object's command, the *my* command may
also invoke non-exported methods.
*my* /methodName/ ?/arg/ /arg/ ...?
NEXT
The *next* command allows methods to invoke the implementation of the
method with the same name in their superclass (as determined by the
normal inheritance rules; if a per-object method overrides a method
defined by the object's class, then the *next* command inside the
object's method implementation will invoke the class's implementation
of the method). The arguments to the *next* command are the arguments
to be passed to the superclass method; this is in contrast to the XOTcl
*next* command, but other features in Tcl 8.5 make this approach viable
and much easier to control. The current stack level is temporarily
bypassed for the duration of the processing of the *next* command; this
is in contrast to the XOTcl version of the *next* command, but it
allows a method to always execute identically with respect to the main
calling context.
*next* ?/arg/ /arg/ ...?
It is an error to invoke the *next* command when there is no superclass
definition of the current method.
SELF
The *self* command allows executing methods to discover information
about the object which they are currently executing in. Without
arguments, the *self* command returns the current fully-qualified name
of the object (to promote backward compatability). Otherwise, it is a
command in the form of an ensemble (though it is not defined whether it
is manipulable with *namespace ensemble*).
The following subcommands of *self* are defined. None of these
subcommands take additional arguments.
caller: Returns a three-item list describing the class, object and
method that invoked the current method, respectively. Syntax:
*self caller*
class: Returns the name of the class that defines the currently
executing method. If the method was declared in the object
instead of in the class, this returns the class of the object
containing the method definition. Syntax:
*self class*
filter: When invoked inside a filter, returns a three-item list
describing the object or class for which the filter has been
registered. The first element is the name of the class or object,
the second element is either *method* (for a method defined in a
class for its instances) or *self.method* (for a method defined
by a single object), and the third element is the name of the
method.
method: Returns the name of the currently executing method. Syntax:
*self method*
namespace: Returns the namespace associated with the current object.
Syntax:
*self namespace*
next: Returns the fully-qualified name of the method that will be
executed when the *next* command is invoked, or an empty string
if there is no superclass definition for the method. Syntax:
*self next*
object: Returns the name of the current object, the same as if the
*self* command is invoked with no arguments. Syntax:
*self object*
target: When invoked from a filter or mixin, returns a two-item list
consisting of the name of the class that holds the target method
and the name of the target method. Syntax:
*self target*
THE OO::DEFINE COMMAND
------------------------
*oo::define* /objectOrClass/ /subcommand/ ?/arg/ ...?
*oo::define* /objectOrClass/ /script/
The *oo::define* command is used to add behavior to objects or classes.
In the second form, /script/ is a Tcl script whose commands are the
subcommands of *oo::define*; this is a notational convenience, as the
two forms are semantically equivalent. (Note that the context in which
/script/ executes is otherwise not defined.)
CLASS-RELATED SUBCOMMANDS
The subcommands of *oo::define* (which may be unambiguously abbreviated
when not in the script form) shall be:
* *abstract* - this is valid only for classes, takes no arguments,
and marks the class so that instances of the class cannot be
created. Subclasses may be created though; abstract-ness is not
inherited.
* *constructor* - this is valid only for classes, takes two
arguments (a *proc*-style argument list, and a body script), and
sets the constructor for the instances of the class to be
executed as defined by the body script after binding the actual
arguments to the call that creates an instance of the class to
the formal arguments listed. The constructor is called after the
object is created (following checks for abstractness) but before
any instance variables are guaranteed to be set. If no
constructor is specified, the constructor will accept exactly the
same arguments as the constructor in the parent class, and will
delegate all the arguments to that parent-class constructor. See
the *method* method for a description of the behaviour of pre-
and postconditions.
*oo::define* /class/ *constructor* /argList/ /body/
?/precondition/? ?/postcondition/?
* *copy* - this creates an exact copy of an object with the given
name. If /name/ is the empty string, a new name will be generated
automatically.
*oo::define* /object/ *copy* /name/
* *destructor* - this is valid only for classes. It defines the
class destructor; a destructor is like a method but takes no
arguments. It is called by the object's *destroy* method, which
is defined automatically and which cannot be overridden. The
syntax is as follows:
*oo::define* /class/ *destructor* /body/
In classic XOTcl, the destructor is simply a method; it must
explicitly call the parent destructor using XOTcl's next command.
In the core OO system, the chain of destructors is called in the
proper sequence automatically and independently of the content of
any particular destructor.
Note that destructors are called whenever the object is deleted
by any mechanism (except when the overall interpreter is deleted,
when execution of Tcl scripts has ceased to be possible anyway).
* *export* - this specifies that the named methods are exported,
i.e. part of the public API of the class's instances. The syntax
is as follows:
*oo::define* /class/ *export* /name/ ?/name/ ...?
An exported method is accessible to clients of the object; an
unexported method is accessible only to the object's own code
through the *my* command.
* *filter* - this subcommand (operating on the class if the object
is a class, and on the object itself otherwise - see the
*self.filter* subcommand for how to force it the other way)
controls the list of filter methods for a class or object. Each
filter method in the list is called when any method is invoked on
the class's instances or the object, and it is up to the filter
to decide whether to invoke the filtered method call (using the
*next* command) or return a suitable replacement value.
*oo::define* /objectOrClass/ *filter* /filterList/
* *filterguard* - this subcommand defines a list of guard
expressions for a filter; the filter is skipped (i.e. the
underlying method call is invoked directly) if any of the guards
returns a false value. Syntax:
*oo::define* /objectOrClass/ *filterguard* /filterName/
/guardList/
* *forward* - this subcommand (operating on the class if the object
is a class, and on the object itself otherwise - see the
*self.forward* subcommand for how to force it the other way)
defines a class method which is automatically forwarded (i.e.
delegated) to some other command, according to a simple pattern.
Each /arg/ is used literally.
*oo::define* /objectOrClass/ *forward* /name/ /targetCmd/ ?/arg/
...?
* *invariant* - this subcommand (only valid for classes) defines a
set of class invariants, scripts that must return a true value
both before and after every method call. This set is inherited by
subclasses. Syntax:
*oo::define* /class/ *invariant* /invariantList/
* *method* - this subcommand (only valid for classes) defines a
class method (i.e. a method supported by every instance of the
class). By default, methods are exported if they start with a
lower-case letter (i.e. any character in \u0061 to \u007a
inclusive) and are not exported otherwise. The optional pre- and
postconditions expressions are evaluated in the context of the
body of the method; the precondition must return a true value for
the method body to actually start executing, and the
postcondition must return a true value after the method body has
executed (unless an error was generated) for a normal method exit
to happen - the default error message (on a false condition
result) is "precondition failed" or "postcondition failed", but
if the conditions return an error message that is used instead.
If only one condition is given, it is the precondition.
*oo::define* /class/ *method* /name/ /args/ /body/
?/precondition/? ?/postcondition/?
* *mixin* - This subcommand defines a mixin for a class or object
(operating on the class if the object is a class, and on the
object itself otherwise - see the *self.mixin* subcommand for how
to force it the other way) which is a way of bringing in
additional method implementations (which may add to or wrap
existing methods) on an /ad hoc/ basis. The list of mixins is
traversed when searching for methods before the inheritance
hierarchy, and mixed-in methods may chain to any methods they
override using the *next* command.
*oo::define* /objectOrClass/ *mixin* /mixinList/
* *mixinguard* - this subcommand defines a list of guard
expressions for a mixin; the mixin is skipped (i.e. the
underlying method call is invoked directly) if any of the guards
returns a false value. Syntax:
*oo::define* /objectOrClass/ *mixinguard* /filterName/
/guardList/
* *parameter* - this subcommand defines a parameter (or
parameters), an instance variable with an identically named and
automatically defined access method. If any /name/ is a
two-element list, the first element is the name of the variable
and the second element is the default value to assign to the
variable.
*oo::define* /class/ *parameter* /name/ ?/name/ ...?
The access methods are always defined something like this, for a
parameter named /bar/ in a class named /foo/:
oo::define foo method bar {args} {
my variable {bar vbl}
if {[llength $args] == 0} {
return $vbl
} else if {[llength $args] == 1} {
return [set vbl [lindex $args 0]]
}
return -code error "wrong # args: ..."
}
* *superclass* - This specifies the superclass (or classes) of a
class. Inheritance will follow the XOTcl pattern (except with a
somewhat different class hierarchy, of course). Syntax:
*oo::define* /class/ *superclass* /classList/
* *unexport* - This specifies that the named methods are
unexported, i.e. private. The syntax is as follows:
*oo::define* /class/ *unexport* /name/ ?/name/ ...?
An exported method is accessible to clients of the object; an
unexported method is accessible only to the object's own code,
through the *my* command.
PER-OBJECT SUBCOMMANDS
The following subcommands are all per-object versions of the class
subcommands listed above. When they are applied to a class, they
operate on the class instance itself as an object, and not on the
instances (current and future) of that class (which is why the
distinction is required).
* *self.class* - This subcommand gets and sets the class of an
object. Changing the class of an object can result in many
methods getting added or removed.
* *self.export* - This increases the set of commands exported by
the object.
* *self.filter* - This is a per-object version of the *filter*
subcommand.
* *self.filterguard* - This is a per-object version of the
*filterguard* subcommand.
* *self.forward* - This is a per-object version of the *forward*
subcommand.
* *self.invariant* - This is a per-object version of the
*invariant* subcommand.
* *self.method* - This is a per-object version of the *method*
subcommand.
* *self.mixin* - This is a per-object version of the *mixin*
subcommand.
* *self.unexport* - This decreases the set of commands exported by
the object.
OTHER COMMANDS IN THE ::OO NAMESPACE
--------------------------------------
OO::CHECK
This controls the types of assertion checked for a particular object.
The following types of assertion may be controlled:
pre: When specifed, states that preconditions should be checked during
the processing of an object's methods.
post: When specifed, states that postconditions should be checked
during the processing of an object's methods.
invariants: When specifed, states that object-defined invariants should
be checked during the processing of an object's methods.
classinvariants:
When specified, states that class-defined invariants should be
checked during the processing of an object's methods.
The set of types of assertion to check is specified as the second
argument to the *oo::check* command, the first argument being the
object to set the assertion checking behaviour of. The special type
*all* can be specified to select all assertion types.
*oo::check* /object/ /assertTypeList/
CORE OBJECTS
==============
The following classes are defined, and are the only pre-constructed
objects in the core system.
OO::OBJECT
------------
The root of the class hierarchy is *oo::object*.
*oo::object* /name/
Constructs a new object called /name/ of class /oo::object/; the object
is represented as a command in the current scope. *oo::object* returns
the fully qualified command name. If /name/ is the empty string,
*oo::object* generates a name automatically that is guaranteed to not
clash with any existing command name.
The name of an object is also the name of a command in the form of an
ensemble where the subcommands of the ensemble are the /exported/
method names of the object.
The new object has two predefined non-exported methods: *eval* and
*variable*. Other subcommands and other behaviour can be added using
*oo::define*.
*oo::object* serves as the base class for all other core OO system
classes.
METHODS
The instances of *oo::object* (i.e. all objects and classes) have the
following methods:
eval: This non-exported method concatenates its arguments according to
the rules of *concat*, and evaluates the resulting script in the
namespace associated with the object. The result of the script
evaluation is the result of the /object/ *eval* method.
/object/ *eval* ?/arg/ /arg/ ...?
variable: This non-exported method takes an arbitrary number of
/unqualified/ variable names and binds the variable with that
name in the object's namespace to the same name in the current
scope. If an argument consists of a two-element list, the first
element is the name of the variable to bind in the object's
namespace, and the second element is the name of the variable to
bind in the current scope.
/object/ *variable* ?/varName/ /varName/ ...?
UNKNOWN METHOD HANDLING
When an attempt is made to invoke an unknown method on any object, the
core then attempts to pass /all/ the arguments (including the command
name) to the public *unknown* method of the object. If no such method
exists, an error message is generated. Instances of the core
*oo::object* class do not have an *unknown* method by default.
OO::CLASS
-----------
This class is the class of all classes (i.e. its instances are objects
that manufacture objects according to a standard pattern). Note that
*oo::object* is an instance of *oo::class*, as is *oo::class* itself.
*oo::class* /name/ ?/definition/?
This creates a new class called /name/; the class is an object in its
own right (of class *oo::class*), and hence is represented as a command
in the current scope. *oo::class* returns the fully qualified command
name. If /name/ is the empty string, *oo::class* generates a name
automatically.
The new class command is used to define objects which belong to the
class, just as *oo::object* is. By default, instances of the new class
have no more behaviour than instances of *oo::object* do; new class
behavior can be added to the class in two ways. First, a /definition/
can be specified when creating the class; second, additional behaviour
can be added to the class using *oo::define*.
The definition, if given, consists of a series of statements that map
to the subcommands of *oo::define*. The following three code snippets
are equivalent; each defines a class called *::dog* whose instances
will have two subcommands: *bark* and *chase*.
# Method 1
oo::class dog
oo::define dog method bark {} {
puts "Woof, woof!"
}
oo::define dog method chase {thing} {
puts "Chase $thing!"
}
# Method 2
oo::class dog
oo::define dog {
method bark {} {
puts "Woof, woof!"
}
method chase {thing} {
puts "Chase $thing!"
}
}
# Method 3
oo::class dog {
method bark {} {
puts "Woof, woof!"
}
method chase {thing} {
puts "Chase $thing!"
}
}
CONSTRUCTOR
The constructor for *oo::class* concatenates its arguments and passes
the resulting script to *oo::define* (along with the fully-qualified
name of the created class, of course).
METHODS
The instances of *oo::class* have the following methods:
create: Creates a new instance of the class with the given name. All
subsequent arguments are given to the class's constructor. The
result of the *create* method is always the fully-qualified name
of the newly-created object. Syntax:
/class/ *create* /objName/ ?/arg/ /arg/ ...?
new: Creates a new instance of the class with an automatically chosen
name. All subsequent arguments are given to the class's
constructor. The result of the *new* method is always the
fully-qualified name of the newly-created object. Syntax:
/class/ *new* ?/arg/ /arg/ ...?
unknown: Classes define an unknown-method handler. This is used to hand
off attempts to create a class using the syntax:
oo::class foo bar
to the *create* or *new* method, depending on whether the class
name is an empty string or not.
OO::DEFINER
-------------
This metaclass (subclass of *oo::class*) arranges for its instances to
have the following methods, each of which is delegated to the
identically-named subcommand of the *oo::define* command described
below, operating on the class instance that is an instance of
*oo::definer*.
abstract, constructor, destructor, export, filter, filterguard,
forward, invariant, method, mixin, mixinguard, parameter,
superclass, unexport
Thus, the following commands are equivalent:
# Method 1
oo::definer dog
oo::define dog method bark {} {
puts "Woof, woof!"
}
# Method 2
oo::definer dog
dog method bark {} {
puts "Woof, woof!"
}
CONSTRUCTOR
The *oo::definer* constructor just passes all its arguments to its
parent constructor (i.e. the *oo::class* constructor).
OO::STRUCT
------------
This class (subclass of *oo::object*) has no default constructor. It
has the following exported methods:
append: This is the analog of the core Tcl *append* command except that
the variable name is resolved in the context of the object's
namespace.
/struct/ *append* /varName/ ?/arg/ /arg/ ...?
array: This is the analog of the core Tcl *array* command except that
the array name is resolved in the context of the object's
namespace.
/struct/ *array* /subcommand/ /varName/ ?/arg/ /arg/ ...?
eval: This is a public exposure of the *eval* method defined by the
*oo::object* class.
exists: This is the analog of the core Tcl *info exists* command except
that the variable name is resolved in the context of the object's
namespace.
/struct/ *exists* /varName/
incr: This is the analog of the core Tcl *incr* command except that the
variable name is resolved in the context of the object's
namespace.
/struct/ *incr* /varName/ ?/increment/?
lappend: This is the analog of the core Tcl *lappend* command except
that the variable name is resolved in the context of the object's
namespace.
/struct/ *lappend* /varName/ ?/arg/ /arg/ ...?
set: This is the analog of the core Tcl *set* command except that the
variable name is resolved in the context of the object's
namespace.
/struct/ *set* /varName/ ?/value/?
trace: This is the analog of the core Tcl *trace* command operating on
variables (no other types of tracable items are supported by this
method) except that variable names are resolved in the context of
the object's namespace.
/struct/ *trace* /subcommand/ ?/arg/ /arg/ ...?
unset: This is the analog of the core Tcl *unset* command except that
the variable name is resolved in the context of the object's
namespace.
/struct/ *unset* ?/varName/ /varName/ ...?
vwait: This is the analog of the core Tcl *vwait* command except that
the variable name is resolved in the context of the object's
namespace.
/struct/ *vwait* /varName/
The following non-exported methods are defined:
var: This method takes one argument, the name of a variable to be
resolved in the context of the object's namespace, and returns
the fully qualified name of the variable such that it can be used
with extensions such as Tk (e.g. for the *label* widget's
*-textvariable* option). This method does not assign any value to
the variable. Syntax:
/struct/ *var* /varName/
It is expected that this convenience method will normally be used
solely through the *my* command.
INTROSPECTION SUPPORT
=======================
The core Tcl *info* command shall be extended in the following ways.
AN [INFO OBJECT] SUBCOMMAND
-----------------------------
An *object* subcommand that shall provide information about a
particular object. Its first argument shall be the name of an object to
get information about, its second argument shall be a subsubcommand
indicating the type of information to retrieve and all subsequent
arguments shall be arguments, as appropriate. The following types of
information shall be available:
args: Returns the list of arguments to a method supported by an object.
*info object* /object/ *args* /method/
body: Returns the body of a method supported by an object.
*info object* /object/ *body* /method/
check: Returns the current list of enabled assertion types for an
object (see the documentation for *oo::check* for the list of
acceptable assertion types).
*info object* /object/ *check*
class: Returns the class of an object, or if /className/ is specified,
whether the object is (directly or indirectly through inheritance
or mixin) an instance of the named class.
*info object* /object/ *class* ?/className/?
default: Returns whether a particular argument to a method has a
default value specified, much as *info default* does for a normal
procedure argument.
*info object* /object/ *default* /method/ /argName/
/defaultValueVar/
filters: Returns the list of filters defined for an object.
*info object* /object/ *filters*
filterguards:
Returns the list of filter-guards for a particular filter.
*info object* /object/ *filterguards* /name/
invariants: Returns the list of invariants defined for an object.
*info object* /object/ *invariants*
isa: Returns boolean information about how an object relates to the
class hierarchy. Supports a range of subcommands to allow the
specification of what sort of test is to be performed:
class: Returns whether the named object is a class.
*info object* /object/ *isa class*
metaclass: Returns whether the named object is a class that is
not of immediate type *oo::class* but rather one of its
subtypes instead.
*info object* /object/ *isa metaclass*
mixin: Returns whether the named object has /mixinClassName/ as
one of its mixins.
*info object* /object/ *isa mixin* /mixinClassName/
object: Returns whether /object/ really names an object.
*info object* /object/ *isa object*
typeof: Returns whether the objec is of type /class/ (i.e. an
instance of that class or an instance of a subclass of
that class).
*info object* /object/ *isa typeof* /class/
methods: Returns the list of methods defined for an object.
*info object* /object/ *methods*
mixins: Returns the list of mixins for an object.
*info object* /object/ *mixins*
post: Returns the postcondition for the named method, or an empty
string if no postcondition has been defined.
*info object* /object/ *post*
pre: Returns the precondition for the named method, or an empty string
if no precondition has been defined.
*info object* /object/ *pre*
vars: Returns the list of all variables defined within the object, or
optionally just those that match /pattern/ according to the rules
of *string match*.
*info object* /object/ *vars* ?/pattern/?
AN [INFO CLASS] SUBCOMMAND
----------------------------
A *class* subcommand that shall provide information about a particular
class. Its first argument shall be the name of a class to get
information about, its second argument shall be a subsubcommand
indicating the type of information to retrieve and all subsequent
arguments shall be arguments, as appropriate. The following types of
information shall be available:
abstract: Returns whether the named class is an abstract class.
*info class* /class/ *abstract*
args: Returns the list of arguments to a method supported by an object.
*info class* /object/ *args* /method/
body: Returns the body of a method supported by an object.
*info class* /object/ *body* /method/
default: Returns whether a particular argument to a method has a
default value specified, much as *info default* does for a normal
procedure argument.
*info class* /object/ *default* /method/ /argName/
/defaultValueVar/
filters: Returns the list of filters defined for an object.
*info class* /object/ *filters*
filterguards:
Returns the list of filter-guards for a particular filter.
*info class* /object/ *filterguards* /name/
instances: Returns a list of all direct instances of the class (but not
instances of any subclasses of the class).
*info class* /class/ *instances*
invariants: Returns the list of invariants defined for an object.
*info class* /object/ *invariants*
methods: Returns the list of methods defined for an object.
*info class* /object/ *methods*
mixins: Returns the list of mixins for an object.
*info class* /object/ *mixins*
parameters: Returns a list of all parameters defined by the class.
*info class* /class/ *parameters*
post: Returns the postcondition for the named method, or an empty
string if no postcondition has been defined.
*info class* /object/ *post*
pre: Returns the precondition for the named method, or an empty string
if no precondition has been defined.
*info class* /object/ *pre*
subclasses: Returns a list of all subclasses of the class, or
optionally just those that match /pattern/.
*info class* /class/ *subclasses* ?/pattern/?
superclasses:
Returns a list of all superclasses of the named class in the
class hierarchy. The list will be ordered in
inheritance-precedence order.
*info class* /class/ *superclasses*
XOTCL FEATURES OMITTED FROM THE CORE OO SYSTEM
================================================
Object::autoname: This is trivially implemented in a small procedure,
and core objects can pick names for themselves and are renameable.
Object::cleanup: This is not an especially well-defined method (what
if the object happens to hold handles to complex resources such as
network sockets; it is not generally possible for the state of the
remote server to be reset) and can be added in any compatability layer.
Object::configure: This feature has been deliberately omitted from the
core object system. This would be value added by any XOTcl extension.
Object::extractConfigureArg: This feature is part of *configure*.
Object::getExitHandler: This feature is not necessary for this
version. If it existed, it would not need to be a part of the base
object.
Object::info: The introspection features are moved into the core
*info* command.
Object::move: This feature is equivalent to the use of the standard
*rename* operation.
Object::noinit: This feature has been deliberately omitted from the
core object system because its use is dependent on the use of other
deliberately-omitted features (i.e. *configure*). This would be value
added by any XOTcl extension.
Object::parametercmd: The core object system always handles parameters
in the same simple way; customisation of this process should be done by
subclasses of *oo::class* that override the *parameter* method.
Object::requireNamespace: It should be possible to do away with this
feature through better integration with the core.
Object::setExitHandler: See the comments for *getExitHandler* above.
Class::__unknown: Auto-loading of unknown classes is handled by the
standard core *unknown* command.
Class::allinstances: This feature is trivially implemented in a small
procedure.
Class::alloc: The core objects have no default behaviour, so the
difference with the basic core class behaviour is moot.
Class::create: Core object creation is a much more sealed process, but
the lack of *configure*-like behaviour means that the complexity of
this method is not necessary. Instead, constructors are called
automatically.
Class::parameterclass: Core object system parameters are not
implemented by classes.
Class::volatile: This feature is omitted.
COPYRIGHT
===========
This document has been placed in the public domain.
-------------------------------------------------------------------------
The following sections are non-normative.
SUGGESTED CLASS HIERARCHY FOR XOTCL SUPPORT
=============================================
The XOTcl /Object/ class should derive from the core *oo::struct*
class. The XOTcl /Class/ class must derive from the core *oo::definer*
and the XOTcl /Object/ classes. This gives the following diagram (core
classes are in lower case with their namespace omitted, XOTcl classes
are in upper case).
+--------+
,-------->| object |
| +--------+
| ,^.
creates | |
| _______|_________
| | |
| +-------+ +--------+
`--| class | | struct |
+-------+ +--------+
,^. ,^.
| |
+---------+ +--------+
| definer | | Object |<-.
+---------+ +--------+ |
,^. ,^. |
|_________________| | creates
| |
+-------+ |
| Class |-----------'
+-------+
Note that *class* instances create *object*s (or subclasses thereof),
but /Class/ instances create /Object/s (or subclasses thereof).
-------------------------------------------------------------------------
TIP AutoGenerator - written by Donal K. Fellows
I know you have spent a lot more time thinking about this than I have, and
this is a pretty large spec to digest all at once. But I have to ask...
Does this model really support delegation-based OO concepts as well as
inheritance? You mention snit and mixins, but I don't see specific
delegation support. The keyword "next," for example, is really a pointer up
an inheritance hierarchy, but there isn't really a mechanism for delegation
(sideways) to objects from other object systems. Maybe its just a nit, but
"next" implies some sort of sideways motion to me. (I've always drawn my
linked lists left-to-right and trees from top to bottom.)
--
Bob Techentin techenti...@NOSPAMmayo.edu
Mayo Foundation (507) 538-5495
200 First St. SW FAX (507) 284-9171
Rochester MN, 55901 USA http://www.mayo.edu/sppdg/
> * Object and class names in the core extension to be all
> lower-case, in line with best common practice.
This may be the "best common practice" but enforcing it is
extremely non-tclish in my view. If I wanted to wear that
straitjacket I'd program in ruby. This should *not* be
present. If someone implementing XOTCL or whatever on top
of this wants to add it, then fine. But it's making a
semantic decision about oo programming and you promised
not to do that.
> MY
> *my* /methodName/ ?/arg/ /arg/ ...?
The asterisks confused me, and I hate the way they make the
code look. If you want to use these names there should be
some easier way to disambiguate them. @my would be one
possibility. *my, %my, !my, even ~my would be better. The
trailing marker serves no purpose and makes it harder to
read.
Better yet would be to make the command itself figure out
the right things to do:
my /methodname/ etc.
and use the more tclish subcommand if you can't disambiguate
without help:
my self /methodname/
"my" is an easy case but this argument primarily applies to
the shadowed tcl commands like:
> append: This is the analog of the core Tcl *append* command except that
> array: This is the analog of the core Tcl *array* command except that
> eval: This is a public exposure of the *eval* method defined by the
> *oo::object* class.
...and so on.
This really bugs me because we already have the :: nonsense in
the language, and scattering "*foo*" around is going to seriously
hit readability.
If necessary, I'd rather see some super command prefixed to the
whole shebang:
oo: append ...
oo: array ...
oo: eval
...and so on.
> exists: This is the analog of the core Tcl *info exists* command except
> that the variable name is resolved in the context of the object's
> namespace.
>
> /struct/ *exists* /varName/
>
> incr: This is the analog of the core Tcl *incr* command except that the
> variable name is resolved in the context of the object's
> namespace.
>
> /struct/ *incr* /varName/ ?/increment/?
...etc. Why not something more readable like:
incr /struct/: /varName/ ?/increment/?
The colon appended to the varname would warn incr to provide the
correct oo behavior.
Actually, it could just key off of the first character if you
drop the *foo* stuff:
incr @/struct/ /varName/ ?/increment/?
...any of these would be more readable, I think. At least
they would reduce the visual and namespace clutter.
--
.-. .-. .---. .---. .-..-.|Experts in Linux: www.WildOpenSource.com
| |__ / | \| |-< | |-< > / |"Making the bazaar more commonplace"
`----'`-^-'`-'`-'`-'`-' `-' |Check out my new novel: "Cloud Realm" at:
home:www.smith-house.org:8000|<-this url + /books/list.html
I think you misunderstand something. There's no "forcing"
happening here. The sentence above just explains why the
root object is named [oo::object] rather than [oo::Object] or
something else. The language still allows you to use whatever
capitalization conventions you like.
>> MY
>> *my* /methodName/ ?/arg/ /arg/ ...?
> The asterisks confused me, and I hate the way they make the
> code look.
The asterisks are just the way to indicate BOLD formatting
in plain text.
Perhaps you would be happier looking at the HTML formatted
version of the document where attempts to indicate formatting
will not get in the way?
--
| Don Porter Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/~DPorter/ NIST |
|______________________________________________________________________|
You can do anything you want - in Tcl, you can use Russian for the
classes and Chinese for the objects, and that's just fine - but the OO
system provided by the core is somewhat more constrained. But what you
do with the core is not constrained that way at all.
>> MY
>> *my* /methodName/ ?/arg/ /arg/ ...?
>
> The asterisks confused me, and I hate the way they make the
> code look. If you want to use these names there should be
> some easier way to disambiguate them. @my would be one
> possibility. *my, %my, !my, even ~my would be better. The
> trailing marker serves no purpose and makes it harder to
> read.
All those *'s and /'s are just a formatting convention. Try the HTML
version at http://tip.tcl.tk/257.html if you prefer. :-)
Donal.
The term you are looking for is "monster". It's been really hard to get
into shape.
> Does this model really support delegation-based OO concepts as well as
> inheritance? You mention snit and mixins, but I don't see specific
> delegation support. The keyword "next," for example, is really a pointer up
> an inheritance hierarchy, but there isn't really a mechanism for delegation
> (sideways) to objects from other object systems. Maybe its just a nit, but
> "next" implies some sort of sideways motion to me. (I've always drawn my
> linked lists left-to-right and trees from top to bottom.)
Delegation is supported through "forward"ing. I suspect I've probably
over-simplified from XOTcl's capabilities there (though anyone who
actually understands what XOTcl does in forwarding should let me know,
or better yet, write better documentation of it!) but I'd had enough of
wrestling with the document every evening so I used the "big axe"
approach to simplification there...
Donal.
Having an OO capability is great, but I think that it would be a shame to abandon
old-fashioned procedural tcl entirely and replace it with an OO language. There are
probably plenty of people who prefer a procedural language, and plenty of cases in which
those who use both procedural and OO languages prefer a procedural language.
In particular, it seems to me that an OO language is not a good idea for an extension
language, Tcl's original use, since such languages are often used by people who are
inexperienced programmers. Object-orientation just adds things for them to learn.
And it isn't as if there is a burning need for yet another OO language - if you want
object-orientation at a roughly similar level, Python, Ruby, Pike, and Lua are
available.
So, to what what extent are we talking about a core OO addition, which can be ignored
if so desired, and to what extent are we talking about an OO replacement for
procedural Tcl?
--
Bill Poser, Linguistics, University of Pennsylvania
http://www.ling.upenn.edu/~wjposer/ bill...@alum.mit.edu
The TIP is to add OO features to the language. Whether or not you use
those features is entirely up to you. Tcl won't lose its procedural nature.
The tip mentions one could adopt it progressively, not because
eventually it will replace the procedural nature of tcl, but because the
author thinks it's a better way to work.
No
> > Having an OO capability is great, but I think that it would be a shame to abandon
> old-fashioned procedural tcl entirely and replace it with an OO language. There are
> probably plenty of people who prefer a procedural language, and plenty of cases in which
> those who use both procedural and OO languages prefer a procedural language.
> In particular, it seems to me that an OO language is not a good idea for an extension
> language, Tcl's original use, since such languages are often used by people who are
> inexperienced programmers. Object-orientation just adds things for them to learn.
> And it isn't as if there is a burning need for yet another OO language - if you want
> object-orientation at a roughly similar level, Python, Ruby, Pike, and Lua are
> available.
>
> So, to what what extent are we talking about a core OO addition, which can be ignored
> if so desired, and to what extent are we talking about an OO replacement for
> procedural Tcl?
It's a core addition, encapsulated in its own "oo" namespace, that you
can use if you like but will definitely not be forced to.
This was a big consideration during the preparation of the TIP - for
many applications Tcl works perfectly well without OO.
Steve
--
Steve Landers Scripting Design Studio
Digital Smarties st...@digital-smarties.com
Perth, Western Australia DigitalSmarties.com
Keith.
Ah, I see. I can get behind that.
> The asterisks are just the way to indicate BOLD formatting
> in plain text.
>
> Perhaps you would be happier looking at the HTML formatted
> version of the document where attempts to indicate formatting
> will not get in the way?
>
> http://tip.tcl.tk/257.html
Yes, Donal pointed me to that - MUCH easier to see.
In the immortal words of Emily Litella: "Never mind."
I think it is a really good idea and I hope it is accepted. When would
it be included? In the 9 series?
Also, does adding the hooks to the core speed things up?
Robert
My plan is to get it into 8.5. If I can.
> Also, does adding the hooks to the core speed things up?
If you're on the inside, so to speak, you can do naughty tricks (aka
breaking Tcl's API model) in a nice way (aka not actually breaking the
API model when viewed across the whole action) to speed things up. The
nature of such tricks varies quite a bit though; we'd need to think in
terms of specific examples to actually describe anything in detail.
A hookable core allows for a half-way house; the extension gets to
adjust some specific piece of functionality to its advantage, but can't
do so as thoroughly as if it was "inside". Some ways of doing things we
don't want to expose... ;-)
Donal.
In 8.5?! Wow...
Robert
Eckhard
Realize that this is what the TIP author desires; it's not been voted on
yet; the TIP is still just a draft.
From my outsiders perspective, this seems like a bit of a long shot to
get into 8.5. Last I heard, the plan was to get 8.5 to beta sometime
this month, and no new features go in after that.
Somebody correct me if I'm wrong.
Not just that, it's still a draft under active revision.
> From my outsiders perspective, this seems like a bit of a long shot to
> get into 8.5. Last I heard, the plan was to get 8.5 to beta sometime
> this month, and no new features go in after that.
That was the plan. I have no idea if it still the plan; we have slipped
in the past and will doubtless do so in the future.
Donal.
Hmm... :-/. Well, still I am looking forward to the day when I have
*one* object system in Tcl and don't have to explore the heaps of
different OO extensions any more to find the best one.
Eckhard
How I understood it, there can still be many OO-systems,
but these will share some common building blocks.
Depending on one's need, however, that base can already be
named an oo-system by itself,
If I could vote, I would vote to let it slip so that the hooks could be
included in 8.5 and I really want to see 8.5 too!
Robert
But for new programs you have something to rely on, something that,
hopefully, the majority of new or existing Tcl developers will use and
that is an official standard. This is *really* important for software
development in 2005.
I mean, other languages come with only one object system. Maybe some
really up-spaced features can not be implemented there, but for 99% of
the developers this situation is quite perfect. They don't need to
figure out which object system is the best and can spend more time in
just getting their daily work done.
The really good thing with this proposed object system is, that you can
do both - get your daily work done *and* implement other object systems
and new futuristic features - while keeping backwards compatibility.
This would be absolutely great and IMHO a huge advantage over
competitor languages.
With programming languages it is like with any computer programs - they
live from their usage. And with this proposal implemented, I assume Tcl
could at least double it's user base... maybe ;-)
Eckhard
I don't know that there is any intent on providing *one* object system
within the Tcl core distribution... or am I misunderstanding this TIP?
I think it would be fair to say that the intent of the TIP is "one very
small object system in the Tcl core". By "very small" I mean that there
won't be many supplied classes or features. Hopefully the code itself
will be very small too. :-)
Donal.
Ah well, fortunately, I'm not a member of the TCT, so there's no need
to convince or satisfy me <smile>...
As you note, the intent is to provide an OO system you can rely on being
there - so developers of (for example) Tcllib and Tklib modules don't
need to invent their own OO system just to implement some useful feature.
In addition, it will support megawidgets and so it will become much
easier to extend / enhance the Tk widget set without having to be a C
code guru.
And, further, it will be a platform for the developers of OO systems to
implement their favourite features - hopefully gaining interoperability
with other OO systems and performance without the need to revert to C as
often.
No one is going to be forced to give up their favourite OO system.
In particular, I'd expect Snit and XoTcl to continue to develop.
Think of it as an enabling technology.
> And so now such a thing is being proposed.
Such a thing should have been there a decade ago - it's one reason why
(in the eyes of many) that Tcl has been left behind.
Steve
Tcl is badly in need of a "standard" OO model that is efficient. I
would try to get it in 8.5. I've used XOTcl in a production
application and it works well.
Tcl is a cool little language, but it's also hard to shake that image.
Tcl seems to be a lot less popular these days. It's a very hard sell
in the corporte world.
Why should people choose Tcl/Tk over Python or Java for application
development?
Python has much more momentum(i.e. can find programmers, libraries,
etc), it's faster, and the OO works quite nicely so you can write
large apps. Tcl namespaces were a life saver but it's simply not
enough. Instance variables!!
I've been playing with Java 1.5 for a couple of weeks. Netbeans 4.1
is EXCELLENT. It has a polished GUI builder that generates the Java
code. Anyone can be up to speed doing basic GUIs in a very short
period of time. Once you look at the code, you realize that writing
GUIs in Java/Swing isn't really that difficult. The JTable widget is
fast, supports printing with a simple JTable.print() method, and, in
general, is more polished than the the Tk widgets (I do love Tablelist
(http://wiki.tcl.tk/5527) and I've got Tktables throughout my app).
Trying to put Tktable in an XOTcl class didn't come out as I'd hoped.
What a mess!
If you like ped (http://wiki.tcl.tk/11604), you'll love the ability to
make code changes then reload into a running program. Ever try using
ped and XOTcl? I can compile Java on the PC and run it on Linux. I
don't need to hunt down all my favorite Tcl packages for the other
platform.
Errr... Back to my point... The "core" Tcl/Tk needs to grow and
improve now, or no one will bother learning it in the very near
future. "Standard" OO is a must. Tile with even more widgets and
enhanced functionality (I'm using the treeview) is a must. Finally, a
free static checker that ships with Tcl (like Python's pycheck) would
be helpful.
What else would help a newbie go from 0 to 60 in 6 seconds, or a pro from
1000 lines to 100,000 lines?
-Mike
--
mmelli...@gmail.com
> I've been playing with Java 1.5 for a couple of weeks. Netbeans 4.1
> is EXCELLENT. It has a polished GUI builder that generates the Java
> code. Anyone can be up to speed doing basic GUIs in a very short
> period of time. Once you look at the code, you realize that writing
> GUIs in Java/Swing isn't really that difficult. The JTable widget is
> fast, supports printing with a simple JTable.print() method, and, in
> general, is more polished than the the Tk widgets (I do love Tablelist
> (http://wiki.tcl.tk/5527) and I've got Tktables throughout my app).
> Trying to put Tktable in an XOTcl class didn't come out as I'd hoped.
> What a mess!
Java is still....Java, though. Doing something like:
set fl [open foo.bar]
fconfigure $fl -encoding binary
set data [read $fl]
close $fl
is a true pain in the *** compared to the elegant simplicity of Tcl.
Perhaps Hecl will be able to help out soon:-)
--
David N. Welton
- http://www.dedasys.com/davidw/
Linux, Open Source Consulting
- http://www.dedasys.com/
Regards,
Arjen
> Bryan Oakley <oak...@bardo.clearlight.com> writes:
>
>
> Tcl is badly in need of a "standard" OO model that is efficient. I
> would try to get it in 8.5. I've used XOTcl in a production
> application and it works well.
>
> Tcl is a cool little language, but it's also hard to shake that image.
> Tcl seems to be a lot less popular these days. It's a very hard sell
> in the corporte world.
>
> Why should people choose Tcl/Tk over Python or Java for application
> development?
>
> Python has much more momentum(i.e. can find programmers, libraries,
> etc), it's faster, and the OO works quite nicely so you can write
> large apps. Tcl namespaces were a life saver but it's simply not
> enough. Instance variables!!
when using the xotcl extension, you can use Object instances
instead of namespaces. objects are the more powerful construct.
> I've been playing with Java 1.5 for a couple of weeks. Netbeans 4.1
> is EXCELLENT. It has a polished GUI builder that generates the Java
> code. Anyone can be up to speed doing basic GUIs in a very short
> period of time. Once you look at the code, you realize that writing
> GUIs in Java/Swing isn't really that difficult. The JTable widget is
> fast, supports printing with a simple JTable.print() method, and, in
> general, is more polished than the the Tk widgets (I do love Tablelist
> (http://wiki.tcl.tk/5527) and I've got Tktables throughout my app).
> Trying to put Tktable in an XOTcl class didn't come out as I'd hoped.
> What a mess!
what was the problem? you might find the xotcl mailing list
as a place for posting xotcl-specific questions...
>
> If you like ped (http://wiki.tcl.tk/11604), you'll love the ability to
> make code changes then reload into a running program. Ever try using
> ped and XOTcl?
ped is not oo aware. I would think that it is not hard to extend
ped to provide support for objects, classes, object/class relations.
there is xotclide (http://wiki.tcl.tk/xotclide) which does this (and
more),
there are in some aspects similar functionalities in the oacs
api browser (which is build around aolserver).
> I can compile Java on the PC and run it on Linux. I
> don't need to hunt down all my favorite Tcl packages for the other
> platform.
we have now quite good distributions such as activestate or
tcl aqua, which all contain ... among other things ... xotcl.
tcl and xotcl are as well in the programming environment shipped
with mac os x tiger. Things are getting better.
>
> Errr... Back to my point... The "core" Tcl/Tk needs to grow and
> improve now, or no one will bother learning it in the very near
> future. "Standard" OO is a must.
you are abolutely right. what the tcl community needs are
reasons to attract fresh people. Most schools teach already
oo programming, sometimes already in high shool. tcl needs some
novel reasons to use it and a highly flexible oo system like
the one from xotcl is a step in this direction. tcl needs as
well more press coverage.
the question seems not only a technical, but as well a political
question.
you may have noticed in postings many sentiments against oo in
general,
you might have as well noticed, that oo gets it own namespace
in the tip. Some people seem to fear that oo will "bloat the system",
slow things down, force a different programming style. There
are already many systems, that implement oo-style functionalities
on the tcl layer (i am not refering to the tcl-only oo systems),
so providing this functionality in a c-based oo framework would
help to save memory and to speed things up.
This is exactly the problem without a standard OO system. If you have
many OO systems, which one should be supported by
- checkers
- compilers
- debuggers
- GUI creation frameworks
- syntaxhighlighters
- code browsers
- etc...
?
It's hardly possible to support them all - not to speak of the fact
that next year there can be another fancy OO system in pure Tcl or
whatever. Not that this is entirely bad, but again, no debugger/checker
etc will work with that.
Again, I'd like to see the TIP in the core rather today than tomorrow,
and I would do my best to support Donal and the TCT.
Eckhard
Nothing in the above paragraph, or the removed paragraphs that follow,
were written by me, FWIW. The quoted text all belongs to Michael Mellinger.
I'm not sure what issues Michael had in particular, I will
agree that xotcl does not lend itself to megawidget
development as easy as other OO modules (specifically, snit).
Part of the effort in "redesigning" xotcl for what the core
really needs is to hopefully address this.
> the question seems not only a technical, but as well a political question.
> you may have noticed in postings many sentiments against oo in general,
> you might have as well noticed, that oo gets it own namespace
> in the tip. Some people seem to fear that oo will "bloat the system",
> slow things down, force a different programming style. There
> are already many systems, that implement oo-style functionalities
> on the tcl layer (i am not refering to the tcl-only oo systems),
> so providing this functionality in a c-based oo framework would
> help to save memory and to speed things up.
While I agree that there is no need to fear adding OO as it
won't be forced on users, some users have very valid reasons
to worry about bloat, based on their use of Tcl (often in
embedded environments). In any case, the reason that I favor
the use of oo::* is that it:
1. is about as short a namespace as you can get
2. is clear in meaning
3. ensures no breakage with existing modules
Remember that we are looking to add this in 8.5 (if all goes
well). People will certainly start using it, at the same
time as they transition from or incorporate other modules
that still use other OO systems where 'class' is already used
(for better or worse). By placing it in oo::, we ensure that
things can work. You can always namespace import the
commands if you want - just beware other parts of the system.
--
Jeff Hobbs, The Tcl Guy
http://www.ActiveState.com/, a division of Sophos
Itcl is supported best of all OO systems. ActiveState's TclDevKit
includes support for Itcl in it's debugger and inspector at least, and
the ased IDE comes with a great code browser. Syntax checking is not
very good supported, though.
I think the reason that the support is not better is, that Itcl is not
included in the core. Other object systems came along (Tix, for instance
comes with an own OO system and I believe Tix is as old as Itcl). People
are unsettled about which OO system get's it largest user base in the
end and don't start to put any work in tools for a particular system.
The more OO systems there are, the higher is the unsureness. That is the
other side of multiple OO systems.
E.g.: I am prepared to put some work into a tool that extracts tagged
comments from Itcl classes and generates HTML output in the form of the
API documentation generated by javadoc. Many tools exist for this, but
none of the existing tools does what I like to have: Put the comments
*before* any class, variable or method so that I can have documented
class interfaces and separate method bodies; and generate a 3-frame HTML
page where the documentation is grouped by namespaces, classes and
methods/variables. Also considering public/private/protected access
levels (I not always want to see private member documentation).
So far so good, but now I hear that OO is likely to go into the core and
that XOTcl gets more adapted and used and there are many more OO systems
- so why start working on my particular dreamtool, when the user base of
object oriented Tcl is splitted over multiple OO systems and it is
likely that the OO system which I want to target looses over another?
I know, I could do it anyway (and I really like to see such a tool) -
but the above facts are not very motivating. For Python this would be
really easy (if it would not have been done already): There is *one*
object system, and every Python programmer will use this one - so every
Python programmer who want's to document his code is likely to step over
my tool and use it, once it is ready. I as a developer of this tool can
rely on the one and only object system (therefore I have to choose
between hundrets of widget systems - "no free lunch" ;-).
I can imagine that many people think this way, and that is why the
support for OO in Tcl development tools is so poor. This is likely to
change when there is one OO system in the core - because this is the one
most people will use probably.
BTW, for me it would be perfectly okay to have Itcl included in the
core, but that is not my decision. At least one OO system can make it
there if the TIP gets accepted, this is great.
Eckhard
But it is not the only part in a program, that does more than
'puts "hello world"'... Tcl needs to be aware of this fact.
Eckhard
I don't know what would happen, but my guess is a smallish OO system
shipped as standard (and maybe even used in the core, or in Tk,
somewhere) has a better chance of getting tool support than any of the
other extensions ever did.
I'm happy to post the complete package, it's a little less than 300
hundred lines of code, with another 300 lines of comments...
Here's an example megawidget...
lappend auto_path .
package require xow
namespace import ::xow::*
namespace import ::xotcl::*
WidgetClass Foo -superclass Widget
Foo instproc init {args} {
next
my instvar widgetpath
pack [label $widgetpath.l]
pack [button $widgetpath.b1]
pack [button $widgetpath.b2]
# add all options of the frame except container, class, & visual
my widgetoption bulkadd [my set widgetproc] -omit {
container class visual
}
# add text, foreground, and background options of the label, but
# rename foreground and background to labelfg and labelbg
my widgetoption bulkadd $widgetpath.l -only {
text {foreground labelfg} {background labelbg} font
}
# now add the options for buttons making every option begin with
# "button" and end with the button number. Don't add the underline
# option and for kicks, change padx and pady option names
for {set i 1} {$i < 3} {incr i} {
my widgetoption bulkadd $widgetpath.b$i \
-prefix "button" -postfix "$i" \
-omit {underline} -translate {
xpadding padx
ypadding pady
}
}
my forward flash1 $widgetpath.b1 flash
my forward flash2 $widgetpath.b2 flash
my forward invoke1 $widgetpath.b1 invoke
my forward invoke2 $widgetpath.b2 invoke
}
pack [Foo .f \
-text "foo!" \
-buttontext1 "Press Me!" \
-buttontext2 "Press Him!"]
foreach o [.f configure] {
puts $o
}
update idletasks
.f flash1
.f invoke2
First, I'd like to start out by saying that I fully support an object
system for modern Tcl. However, I find the proposed syntax contained in
this TIP hard to accept.
> HELPER COMMANDS
> -----------------
>
> MY
>
> The *my* command allows methods of the current object to be called
> during the execution of a method, [...]
...
>
> SELF
>
> The *self* command allows executing methods to discover information
> about the object which they are currently executing in. [...]
I'd really like to plead that these be interchanged. To me, this seems
more "natural" to read:
set type [my type]
set class [my class]
set result [self methodName arg1 arg2]
In English, "my" refers to properties that belong to you (i.e., "my
name" or "my address" or "my parents") and "self" has traditionally been
used to refer to the object which the current scope belongs to. (Other
than "self", "this" is also a common token, but I do prefer "self".)
...
While there's lots of nits to pick in this TIP, I'd like to generalize
mine into "resource management and cleanup" -- in long-running Tcl
scripts, such as those inside a web server such as AOLserver, it is
critical to have full support for proper resource management and
cleanup. To this end, I think the following (which is not a complete
list) would be necessary:
- instances of oo::class need a "destroy" method which destroys the
class (and with "-force", possibly all object instances of that
class).
- "info objects" and "info classes" returning a Tcl list of all
objects instantiated and classes defined, respectively.
In short, I'd need a way to very simply ask the Tcl interpreter "what
objects and classes did the programmer not clean up" then proceed to
destroy them all. Of course, there'll need to be a way to "protect"
objects and classes from automatic cleanup for those cases where they
really WERE intended to live across interp. reuse, but that's generally
an edge case: the majority case of objects not being cleaned up by the
programmer are unintentional resource leaks.
All that said, since I made the comment about syntax earlier, I much
prefer stooop's (and through similarity, [incr Tcl]'s) syntax. If
stooop and dict's for member variables proves fast enough, I think it
provides solutions for most of my needs, I think.
Perhaps what needs to really be done is a better stooop tutorial, along
with perhaps a non-trivial, free, open source application implemented
using stooop to demonstrate it. If, it turns out, that there are real
needs to make stooop either perform acceptably fast (optimizations) or
to make certain features possible (like what?!), then I think TIP'ing
those specific enhancements make sense.
-- Dossy
--
Dossy Shiobara do...@panoptic.com | http://dossy.org/
Panoptic Computer Network http://panoptic.com/
"He realized the fastest way to change is to laugh at your own
folly -- then you can let go and quickly move on." (p. 70)
These two features are implementable on top of facilities that are
already there through a combination of the enhanced standard [info]
command and the ability to dynamically modify a class (oo::class
inherits from oo::object, and so has a 'destroy' method but you'd want
to override that, which you can do).
I've not put the behaviour in by default because that makes it quite
difficult to implement the XOTcl behaviour on top. This naturally means
that I'd prefer doing this sort of thing using a subclass of oo::class
but that's a separate issue.
Donal.
just for the record: xotcl classes have a destroy method, since classes
are certain types of objects. in xotcl it is trivial to define -force
e.g. via non positional arguments to behave like you indicated.
> - "info objects" and "info classes" returning a Tcl list of all
> objects instantiated and classes defined, respectively.
"info objects" is in xotcl "Object allinstances", "info classes" is
"Class allinstances". the method allinstances computes the closure of
the instances and the instances of all subclasses. If one wants only
direct instances, one can use e.g. "Class info instances", or in some
application, one can use "Person info instances" or "VolatialeObjects
allinstances". The point is, that one needs freequently not only "info
objects", but certain objects/classes of some types.
Therefore a general "info objects" is not very appealing.
There is a difference between the functionality and how you interact
with it. Does xotcl *really* look that tcl-sh to everyone else?
No, and that doesn't take anything away from the XOtcl folks. I think
Snit is a much better fit with Tcl. That is my opinion. I hope anything
in the core can make Snit better and faster because that is what I will
be using.
Robert
I agree with you there. Once I read the XOtcl tutorial to figure out
what it is all about. There are some neat features that seem useful to
me on a first glance, like mixin objects and meta objects.
But the more I think about it, the more I feel that these things could
leed to a total confusion in a big program and make it complicated to
debug, because it is difficult to follow the runtime behaviour. You not
only have to trace the inner state of particular objects at certain
runtime points, you also have to follow all the mixin/filter and
whatever actions that happened until this point.
I can not imagine yet, that this is easier... but maybe it is just
different. And since I am "fluent" in Java and C++, I just feel much
more comfortable with Itcl at the moment.
But the TIP sounds not like "including XOTcl in the core". It sounds
like we get a small, simple and extensible object system we can rely on
- And that fits very well in the picture of Tcl ;)
Eckhard
XOTcl's syntax is relatively close to all other OO packages in Tcl. Ie.
object method arg arg ...
what is the problem? Yes, that is different from Java and C++, but their
syntax would absolutely not fit in well with Tcl but feel more like a hack.
If you are referring to class definition I can see why it was done that way
in XOTcl. It reflects the dynamic nature of the extension and you quickly
get used to it. It also saves from one extra indentation level which in Java
is mostly useless. The TIP does, however, provide a way of collecting method
definitions into one block, thus bringing it closer to what Java looks like.
I gather that is the goal. The TIP does not propose an OO system to replace
everything but one which can be used alone in a minimalistic way, or to
build other systems.
Consider that most programs and classes will follow the traditional
inheritance patterns you use. Anyway, that is the nature of dynamic
language. Anything can potentially be changed. This is true for Tcl itself.
> I can not imagine yet, that this is easier... but maybe it is just
> different. And since I am "fluent" in Java and C++, I just feel much
> more comfortable with Itcl at the moment.
I started OO coding with Java and believe me, once you get used to a dynamic
object system there is no going back. Java to me feels very clunky nowadays.
Again it is important to reiterate that with the TIP you can use iTcl. In
fact, it will probably benefit from it with greater performance and
flexibility.
I see XOTcl really of value when dealing with large complex projects
and think that, precisely because of XOTcl's capabilities for filters,
mixings and metaclasses, you could have a Traceable Metaclass or any
other scheme as the ones depicted in the tutorial that can help you
with tracing what is going on at run time.
It's the next level of syntax (the interpretation of the arguments)
where XOTcl has a bit of a tendency to head off into the long grass. In
particular, the 'configure' method is both highly funky and critical to
many of the design decisions in XOTcl I've found. The implications of
dropping it (which was nonetheless necessary) have been a large part of
why it's taken such a long time to get #257 into a more cooked state.
Donal.
> It's the next level of syntax (the interpretation of the arguments)
> where XOTcl has a bit of a tendency to head off into the long
> grass. In particular, the 'configure' method is both highly funky
> and critical to many of the design decisions in XOTcl I've
> found. The implications of dropping it (which was nonetheless
> necessary) have been a large part of why it's taken such a long time
> to get #257 into a more cooked state.
That was my reaction when I looked at XOTcl (which was years ago,
admittedly). A few changes ought to make it much more immediately
accessible (changing "instproc" to "method", and that kind of thing).
Basically it seemed OK, though, and suits Tcl much more than [incr
Tcl], IMHO. I think #257 will work out well.
> > XOTcl's syntax is relatively close to all other OO packages in Tcl. Ie.
> > object method arg arg ...
>
> It's the next level of syntax (the interpretation of the arguments)
> where XOTcl has a bit of a tendency to head off into the long grass. In
> particular, the 'configure' method is both highly funky and critical to
> many of the design decisions in XOTcl I've found.
not being a native speaker i wonder what "head off into the long grass"
means technically, esp. when you refer to xotcl's configure method.
from earlier
discussions, i had the impression that you dislike the fact that one
can
call arbitrary methods via configure. Is that what you are refering to?
Regardless of the tip, we are trying to improve xotcl constantly.
It's probably more accurate to say that if someone in the incr tcl community
provides appropriate modifications, itcl might benefit from internal use of
the code for this TIP.
--
<URL: http://wiki.tcl.tk/ > Indescribable,uncontainable,all powerful,untameable
Even if explicitly stated to the contrary, nothing in this posting
should be construed as representing my employer's opinions.
<URL: mailto:lvi...@gmail.com > <URL: http://www.purl.org/NET/lvirden/ >