TIP 478: Add Expected Class Level Behaviors to oo::class

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Author:         Gerald Lester <Gerald.Lester@gmail.com>
Author:         Donal K. Fellows <donal.k.fellows@manchester.ac.uk>
State:          Final
Type:           Project
Vote:           Done
Created:        18-Oct-2017
Post-History:   
Keywords:       Tcl
Tcl-Version:    8.7
Votes-For:      DKF, AK, JD, SL, JN
Votes-Against:  none
Tcl-Branch:     tip-478

Abstract

TclOO provides a native OO system for Tcl. This TIP is to enhance it to meet some of the common expectations of people coming in from other languages.

Rationale

Most other OO systems provide for class variables which are accessible and shared by all members of a class and class methods which only have access to class variable and not to instance/object variables. They also provide a way to initialize the class variables. This TIP seeks to add them into TclOO.

Proposal

The proposal is to add some or all of the functionality of oo::util, in particular the classvariable and classmethod. Additionally, it is proposed to add an initialise class definition command to all the initialization of class variables.

New Basic Features

The myclass command (created in each object's namespace) allows any method (exported or unexported) of the current class of the owning object to be invoked. It is like using info object class to look up the class of the current object, info object namespace to find the namespace of the class, then using the my command in that namespace (assuming that it hasn't been renamed). This is implemented in C so it is fast, effective, and robust.

New Metaclasses

The oo::singleton metaclass only allows a single instance of the class that it is mixed into to exist at a time. It hides the create method on the class and implements a cache so that the new method will only create an instance of the class if there is not an existing instance. Subclasses of the singleton class are not restricted.

The oo::abstract metaclass does not allow any instances of the class that it is mixed into to be created, hiding the instantiation methods (create, new, etc.) of that class. The class can still be inherited from and those subclasses can be instantiated.

New Definitions

The classmethod class definition creates a method that can be used when invoked against its defining class or any of its subclasses. When a class method is invoked on the class or any of its subclasses, the current object (i.e., the object reported by self) will be the class on which it is invoked (i.e., the subclass if it is invoked against a subclass). When a class method is invoked on an object that is an instance of the class or any of its subclasses, the current object will be the current class of the object on which the method was invoked. Thus:

oo::class create Super {
    classmethod x {} {puts "self is [self]"}
}
oo::class create Sub {superclass Super}
set instSuper [Super new]
set instSub [Sub new]

Super x;        # prints 'self is ::Super'
Sub x;          # prints 'self is ::Sub'
$instSuper x;   # prints 'self is ::Super'
$instSub x;     # prints 'self is ::Sub'

The initialise class definition evaluates a script in a context where it can access the class's namespace, allowing for easier initialisation of a class than simply overriding its constructor. It has the alternate name, initialize. In particular, this makes it much easier to create procedures and variables in that namespace.

New Helper Commands

The callback/mymethod command, available within methods, takes the name of a method and zero-or-more arguments and returns a script fragment that will allow that method easy to invoke from a callback (e.g., a variable trace, chan event callback, or Tk event binding). The command will be available with both names.

The classvariable command, available within methods, binds a local variable to a variable within the namespace of the class that defined the method.

The link command, available within methods, creates a binding for methods so that calling the command with the given name is equivalent to calling my $name instead. It can link multiple methods with one call, one per argument, and those created commands can be renamed without losing the link; if an argument is a two-element list, the first element is the name of the method and the second is the name of the command (which will be resolved relative to the current namespace if it is not an absolute command name).

Reference Implementation

Reference implementations are mentioned in the proposal section. Additionally, this is a reference implementation for initialise:

proc ::oo::define::initialise {body} {
    set clsns [info object namespace [uplevel 1 self]]
    tailcall apply [list {} $body $clsns]
}

This is a reference implementation for oo::abstract:

oo::class create oo::abstract {
    superclass oo::class
    unexport create createWithNamespace new
}

Copyright

This document has been placed in the public domain.

History