TIP #257: Object Orientation for Tcl
TIP #257: OBJECT ORIENTATION FOR TCL
======================================
Version: $Revision: 1.2 $
Author: Donal K. Fellows <donal.k.fellows_at_manchester.ac.uk>
Will Duquette <will_at_wjduquette.com>
Steve Landers <steve_at_digitalsmarties.com>
Jeff Hobbs <jeffh_at_activestate.com>
Kevin Kenny <kennykb_at_users.sourceforge.net>
Miguel Sofer <mig_at_utdt.edu>
State: Draft
Type: Project
Tcl-Version: 8.5
Vote: Pending
Created: Monday, 26 September 2005
URL: http://purl.org/tcl/tip/257.html
WebEdit: http://purl.org/tcl/tip/edit/257
Post-History:
Obsoletes: TIP #50
-------------------------------------------------------------------------
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
.
Relevant Pages
- [Fwd: TIP #257: Object Orientation for Tcl]
... designed specifically to allow classic XOTcl to be built on top. ... the time has come for the core to provide OO support. ... definition is to be moved to a separate utility command, ... every class and every object has an associated namespace. ... (comp.lang.tcl) - Re: xotcl nested namespaces/objects
... package require XOTcl ... Then I run the following command: ... namespace "my-test" is not there. ... One problem with Tcl is that you can ... (comp.lang.tcl) - Silent overriding of the command?
... I always have thought that in order to replace Tcl command with own ... Is there way to invoke Tcl core command regardless of what ... user have messed up in global namespace, ... (comp.lang.tcl) - Re: a new language? was (Re: is tcl/tk dying out?!)
... added to the core as well. ... you'd could create some very powerful idioms for ... got Tcl lineage, but it's decidedly not your grandfather's Tcl. ... without having had XOTcl go before and work out much of the really ... (comp.lang.tcl) - Re: Invoking External Batch Program on Multiple Core Machine
... (Although free version of command line .NET compiler is available, so if some new feature would greatly ... work (you pass it a file in the command line, it runs for 2-45 minutes and spits out another file). ... This is very annoying since I have a dual core machine and would like to get my work done as quickly as possible. ... You can use the Process class to start a new process with a specific command, and you can even set the ProcessorAffinity on the class instance to control what CPUs it runs on. ... (microsoft.public.dotnet.languages.csharp) |
|