Main Contents »

Copyright © 2015 Ashok P. Nadkarni. All rights reserved.

Tcl offers programming constructs that not only support object-oriented programming but also provide the infrastructure for building extended OO systems on top. This chapter provides an introduction to these facilities.

1. Introduction

This chapter describes Tcl features that support object oriented programming. It does not go into detail about what constitutes object oriented programming (depends on who you ask), what its benefits are (depends on who you ask) or how your classes should be designed (depends on …​ you get the point). The number of words written on the topic in all likelihood exceeds the total number of objects in the universe and we don’t want to add to that number. You can always refer there. Also, the author is hardly an expert in object oriented design and it would really be a case of the one-eyed leading the blind.

Nevertheless, as we go along we will briefly describe some basic concepts for the benefit of the reader who really is completely unexposed to OO programming.

A bit of history

One of the knocks against Tcl in its early days was that it did not support object oriented programming. This criticism was both incorrect and unfair because Tcl did in fact support not one, but several, OO implementations. This misconception was at least partly due to the fact that these OO systems did not come as part of the core language, but rather were implemented as extensions or packages. In fact, writing an OO system in Tcl became a rite of passage for many Tcl programmers.

Some of these systems became fairly widely used and remain so today:

  • IncrTcl's name was a take-off on C++ and so is its design. It was intended to make programmers used to that language feel at home. It was one of the earliest Tcl-based OO extensions to be widely used.

  • Snit (Snit’s Not Incr Tcl) is a popular OO implementation which is particularly useful towards building Tk widgets.

  • XoTcl and its successor nx are OO implementations designed for research into dynamic OO programming.

The experience gained from these system led to the implementation of a OO system in the Tcl core - TclOO. This became part of the Tcl 8.6 release and is also available as an extension for Tcl 8.5. TclOO can be used as a standalone OO system by itself. However, one of its goals was also to provide the base facilities required for layering other OO systems on top.

The Tcl based OO programming described in this book is based on TclOO.

Most of the example code in this chapter is based on a framework for modeling banks. Our bank has accounts of different types, such as savings and checking, and which allow operations such as deposits and withdrawals. Some of these are common to all account types while others are unique. We have certain privileged customers who get special treatment and we have to also follow certain directives from Big Brother.

No, Citibank cannot run its operations based on our framework but it suffices for our illustrative purposes.

2. Classes

2.1. Objects and classes

The core of OO programming is, no surprise, an object. An object, often a representation of some real world entity, captures state (data) and behaviour which is the object’s response to messages sent to it. In most languages, implementation of these messages involves calling methods which are just function calls with a context associated with the object. For example, the state contained in an object representing a bank account would include items such as the current balance and account number. The object would respond to messages to deposit or withdraw funds.

A class is (loosely speaking) a template that defines the data items and methods (collectively called members) encapsulated by objects of a specific type. More often than not, creating a object of the class, often known as instantiating an object, is one of the duties of the class.

Not every OO system has, or needs, the notion of a class. Prototype based systems instead create objects by “cloning” an existing object - the prototype - and defining or modifying members.

TclOO provides facilities to support both the classy and the classless[1] models.

2.2. Creating a class

Classes are created in TclOO using the oo::class create command. Let us create a class, Account, that models a banking account.

% oo::class create Account
→ ::Account

This creates a new class Account that can be used to create objects representing bank accounts.

Note

The class Account is actually just another Tcl command and could have been created in any namespace we choose, not necessarily the global one. For example, either

oo::class create bank::Account
namespace eval bank {oo::class create Account}

would create a new class Account in the bank namespace, entirely unrelated to our Account class in the global namespace.

There is however no class definition associated with our Account class and therefore, there is as yet no state or behaviour defined for objects of the class. That is done through one or more class definition scripts. We will look at the contents of these definition scripts throughout this chapter, but for now, simply note that class definitions can be built up in incremental fashion. A definition script can be passed as an additional argument to the oo::class create command, in the form

oo::class create CLASSNAME DEFINITIONSCRIPT

and also through one or more oo::define commands which take the form

oo::define CLASSNAME DEFINITIONSCRIPT

Thus the statements

oo::class create CLASSNAME DEFINITIONSCRIPT

and

oo::class create CLASSNAME
oo::define CLASSNAME DEFINITIONSCRIPT

are equivalent.

We will see both forms of class definition as we go along. As is generally the case, Tcl has the flexibility to fit your programming style.

2.3. Destroying classes

A class, as we shall see later, is also an object and like all objects can be destroyed.

% Account destroy

This will erase

  • the definition of the Account class,

  • any classes that inherit from, or mixin, the Account class

  • all objects belonging to all destroyed classes.

Not commonly used in operational code, this ability to destroy classes is sometimes useful during interactive development and debugging to reset to a known clean state.

We will be using the Account class throughout the chapter so let us recreate it before we move on.

% oo::class create Account
→ ::Account

2.4. Defining data members

In our simple example, the state for an account object includes an account number that uniquely identifies it and the current balance.

We will need data members to hold this information and we define them through a class definition script passed to oo::define.

% oo::define Account {
    variable AccountNumber Balance 1
}
1 The author uses mixed case for data members to avoid conflicts with names of method arguments and local variables.

This defines the data members for the class as per-object variables. AccountNumber and Balance are then visible within all methods of the class and can be referenced there without any qualifiers or declarations.

There can be multiple variable statements, each defining one or more data members.

Data members do not have to be declared using variable in a class definition script. They can also be declared within a method using the my variable command which we show later.

Caution Note the difference between the variable statement in the context of a class definition and the variable command used to define namespace variables. They both have very similar function but the former only defines data member names, not their values whereas the latter defines names in namespace as well as their initial values.

2.5. Defining methods

Having defined the data members, let us move on to defining the methods that comprise the behaviour of an Account object. An Account object responds to requests to get the current balance and to requests for depositing and withdrawing funds.

Methods are defined through the method command which, like variable, is executed as part of a class definition script.

oo::define Account {
    method UpdateBalance {change} {
        set Balance [+ $Balance $change]
        return $Balance
    }
    method balance {} { return $Balance }
    method withdraw {amount} {
        return [my UpdateBalance -$amount]
    }
    method deposit {amount} {
        return [my UpdateBalance $amount]
    }
}

As you see, a method is defined in exactly the same manner as proc defines a Tcl procedure. Just like in that case a method takes an arbitrary number of arguments including a variable number of trailing arguments collected as the args variable. The difference from a procedure lies in how it is invoked and the context in which the method executes.

Method context

A method runs in the context of its object’s namespace. This means the object data members such as Balance, defined through variable, are in the scope of the method and can directly be referenced without any qualifiers as seen in the method definition above.

The method context also makes available several commands - such as self, next and my - which can only be called from within a method. We will see these as we proceed with our example but for now note the use of my to refer to a method of the object in whose context the current method is running.

Method visibility

Another point about method definitions concerns method visibility. An exported method is a method that can be invoked from outside the object’s context. A private method on the other hand, can only be invoked from within another method in the object context. Methods that begin with a lower case letter are exported by default. Thus in our example, deposit and withdraw are exported methods while UpdateBalance is not. Method visibility can be changed by using the export and unexport commands inside a oo::define class definition script. Thus

oo::define Account {export UpdateBalance}

would result in UpdateBalance being exported as well.

Deleting methods

Method definitions can be deleted at any time with the deletemethod command inside a class definition script.

Warning The following code snippet will crash Tcl versions prior to 8.6.2 due to a bug in the Tcl core.
oo::class create C {method print args {puts $args}}
C create c
c print some nonsense
oo::define C {deletemethod print}
c print more of the same

Deletion of methods from classes is rarely used. However, deletion of methods from objects is sometimes useful in specialization of objects.

2.6. Constructors and destructors

There is one final thing we need to do before we can start banking operations and that is to provide some means to initialize an Account object when it is created and perform any required clean up when it is destroyed.

These tasks are performed through the special methods named constructor and destructor. These differ from normal methods in only two respects:

  • They are not explicitly invoked by name. Rather, the constructor method is automatically run when an object is created. Conversely, the destructor method is run when the object is destroyed.

  • The destructor method definition differs from other methods in that it only has a single parameter - the script to be run. It does not have a parameter corresponding to arguments.

For our simple example, these methods are straightforward.

oo::define Account {
    constructor {account_no} {
        puts "Reading account data for $account_no from database"
        set AccountNumber $account_no
        set Balance 1000000
    }
    destructor {  1
        puts "[self] saving account data to database"
    }
}
1 Note the syntax of the destructor definition

Both constructors and destructors are optional. They do not have to be defined in which case TclOO will simply generate empty methods for them.

2.7. The unknown method

Every object has a method named unknown which is run when no method of that name is defined for that object (actually in the method chain for that object as we see later).

The definition of the unknown method takes the form

oo::define CLASSNAME {
    method unknown {target_method args} {…​implementation…​}
}

The unknown method is passed the name of the invoked method as its first argument followed by the arguments from the invocation call.

The default implementation of this method, which is inherited by all objects from the root oo::object object, raises an error. Classes and objects can override the default implementation method to take some other action instead.

An example of its use is seen in the COM client implementation in TWAPI. The properties and methods exported from a COM component are not always known beforehand and in fact can be dynamically modified. The TclOO-based wrapper for COM objects defines an unknown method that looks up method names supported by a COM component the first time a method is invoked. If found, the lookup returns an index into a function table that can then be invoked through the ComCall method. The implementation of unknown looks like

oo::define COMWrapper {
    method unknown {method_name args} {
        set method_index [COMlookup $method_name]
        if {$method_index < 0} {
            error "Method $method_name not found."
        }
        return [my ComCall $method_index {*}$args]
    }
}

(This is a greatly simplified, not entirely accurate or correct, description for illustrative purposes.)

2.8. Modifying an existing class

As we have seen in previous sections, you can incrementally modify a class using oo::define. Practically nothing about a class is sacred - you can add or delete methods, data members, change superclasses or mixins, and so on.

The question then arises as to what happens to objects that have already been created if a class is modified. The answer is that existing objects automatically “see” the modified class definition so for example any new methods can be invoked on them. Or if you add a mixin or a superclass, the method lookup sequence for the object will be appropriately modified.

However some care should be taken when modifying a class since existing objects may not hold all state expected by the new class. For example, the new constructors are (obviously) not run for the existing objects and thus some data members may be uninitialized. The modified class code has to account for such cases.

3. Working with objects

Having defined our model, we can now begin operation of our bank to illustrate how objects are used.

3.1. Creating an object

An object of a class is created by invoking one of two built-in methods on the class itself. The create method creates an object with a specific name. The new method generates a name for the created object.

% set acct [Account new 3-14159265]
→ Reading account data for 3-14159265 from database
  ::oo::Obj122
% Account create smith_account 2-71828182
→ Reading account data for 2-71828182 from database
  ::smith_account

Creating an object also initializes the object by invoking a its constructor.

The created objects are Tcl commands and as such can be created in any namespace.

% namespace eval my_ns {Account create my_account 1-11111111}
→ Reading account data for 1-11111111 from database
  ::my_ns::my_account
% Account create my_ns::another_account 2-22222222
→ Reading account data for 2-22222222 from database
  ::my_ns::another_account

Note that my_account and my_ns::my_account are two distinct objects.

3.2. Destroying objects

Objects in Tcl are not garbage collected as in some other languages and have to be explicitly destroyed by calling their built-in destroy method. This also runs the object’s destructor method.

% my_ns::my_account destroy
→ ::my_ns::my_account saving account data to database

Any operation on a destroyed object will naturally result in an error.

% my_ns::my_account balance
→ invalid command name "my_ns::my_account"

Objects are also destroyed when its class or containing namespace is destroyed. Thus

% namespace delete my_ns
→ ::my_ns::another_account saving account data to database
% my_ns::another_account balance
→ invalid command name "my_ns::another_account"

generates an error as expected.

3.3. Invoking methods

An object in Tcl behaves like an ensemble command of the form

OBJECT METHODNAME args…​.

This is the form used to invoke a method on the object from code “outside” the object.

% $acct balance
→ 1000000
% $acct deposit 1000
→ 1001000

As discussed earlier, when calling a method from another method in the same object context, the alias my is used to refer to the current object. So the deposit method we saw earlier calls the UpdateBalance method as:

my UpdateBalance $amount

3.4. Accessing data members

Data members are not directly accessible from outside the object. Methods, such as balance in our example, have to be defined to allow callers to read and modify their values. Many OO-purists, and even non-purists like the author, believe this to be desirable.

However, Tcl being Tcl, it is always possible to add a variable access capability using the fact that each object has a private namespace that can be retrieved through introspection. Thus,

% set [info object namespace $acct]::Balance 5000
→ 5000
% $acct balance
→ 5000

This practice breaks encapsulation and is not recommended. However, some OO systems layered on top of TclOO do offer this feature in a structured manner that does not explicitly expose internal object namespaces. These are however not discussed here.

A good alternative is to automatically define accessor methods for public variables without the programmer having to explicitly do so. One such implementation is described in the Lifecycle Object Generators paper.

4. Inheritance

The defining characteristic of OO systems is support for inheritance. Inheritance refers to the ability of a derived class (also refered to as a subclass) to specialize a class - called its base class or superclass - by extending or modifying its behaviour.

Thus in our banking example, we may define separate classes representing savings accounts and checking accounts, each inheriting from the base account and therefore having a balance and methods for deposits and withdrawal. Each may have additional functionality, for example check writing facilities for the checking account and interest payments for the savings account.

The intention behind inheritance is to model is-a relationships. Thus a checking account is a bank account and can be used at any place in the banking model where the behaviour associated with a bank account is expected. This is-a relation is key when deciding whether to use inheritance or some other facility such as mix-ins.

Let us define our SavingsAccount and CheckingAccount. Instead of using oo::define as before, we will provide the full class definition as part of the oo::class command itself.

oo::class create SavingsAccount {
    superclass Account
    variable MaxPerMonthWithdrawals WithdrawalsThisMonth
    constructor {account_no {max_withdrawals_per_month 3}} {
        next $account_no
        set MaxPerMonthWithdrawals $max_withdrawals_per_month
    }
    method monthly_update {} {
        my variable Balance
        my deposit [format %.2f [* $Balance 0.005]] 1
        set WithdrawalsThisMonth 0
    }
    method withdraw {amount} {
        if {[incr WithdrawalsThisMonth] > $MaxPerMonthWithdrawals} {
            error "You are only allowed $MaxPerMonthWithdrawals withdrawals a \
                month"
        }
        next $amount
    }
}
oo::class create CheckingAccount {
    superclass Account
    method cash_check {payee amount} {
        my withdraw $amount
        puts "Writing a check to $payee for $amount" 2
    }
}
1 Monthly interest
2 Pretend we are writing a check

The superclass command in the class definition establishes that SavingsAccount and CheckingAccount inherit from Account. This statement by itself means they will behave exactly like the Account class, with the same methods and variables defined. Further declarations will extend or modify the class behaviour.

4.1. Methods in derived classes

Methods available in the base class are available in derived classes as well. In addition, new methods can be defined such as cash_check and the monthly_update in our example, that are only present on objects of the derived class.

If the derived class defines a method of the same as a method in the base class, it overrides the latter and will be called when the method is invoked on an object of the derived class. Thus the withdraw method of the SavingsAccount class overrides the withdraw method of the base Account class. However, we are just modifying the original method’s functionality with an additional condition, not replacing it. Therefore, after making the check we want to just pass on the request to the base class method and not duplicate its code. This is done with the command next which invokes the next method in the which in this case is the superclass method with the same name as the current method. This method chaining is actually only an example of broader mechanism we will explore in detail later.

Constructors and destructors are also chained. If a derived class does not define a constructor, as is true for the CheckingAccount class, the base class constructor is invoked when the object is created. If the derived class does define a constructor, that is invoked instead and it is up to that constructor to call the base class constructor using next as appropriate. Destructors behave in a similar fashion.

Note that next may be called at any point in the method, not necessarily in the beginning or the end.

4.2. Data members in derived classes

Derived classes can define new data members using either variable in the class definition or my variable within a method as in withdraw.

Caution Because data members are always defined in the namespace of the object, you have to careful about conflicts between variables of the same name being defined in a base class and a derived class if they are intended to represent different values.

Data members defined in a parent (or ancestor) class are also accessible within a derived class but they have to be brought within scope of the method through the variable declaration in the derived class definition or the my variable statement within a method as is done in the implementation of monthly_update. Although we use a direct variable reference there for expository purposes, in the interest of data hiding and encapsulation, direct reference to variables defined in ancestors should be avoided if possible. It would have been better to write the statement as

my deposit [format %.2f [* [my balance] $rate]]

Let us try out our new accounts.

% SavingsAccount create savings S-12345678 2
→ Reading account data for S-12345678 from database
  ::savings
% CheckingAccount create checking C-12345678
→ Reading account data for C-12345678 from database
  ::checking
% savings withdraw 1000
→ 999000
% savings withdraw 1000
→ 998000
% savings withdraw 1000 1
→ You are only allowed 2 withdrawals a month
% savings monthly_update
→ 0
% checking cash_check Payee 500 2
→ Writing a check to Payee for 500
% savings cash_check Payee 500 3
→ unknown method "cash_check": must be balance, deposit, destroy, monthly_updat...
1 Overridden base class method
2 Method defined in derived class
3 Check facility not available for savings

4.3. Multiple inheritance

Imagine our bank is actually a full financial services firm that also provides stock trading services. Accordingly we have a class corresponding to a brokerage account.

oo::class create BrokerageAccount {
    superclass Account
    method buy {ticker number_of_shares} {
        puts "Buy high" 1
    }
    method sell {ticker number_of_shares} {
        puts "Sell low" 2
    }
}
1 Buy high…​
2 …​sell low. Historically, the author’s trading strategy

The company now decides to make it even more convenient for customers to lose money participate in the stock market. So we come up with a new type of account, a Cash Management Account (CMA), which combines the features of the checking and brokerage accounts. We can model this in our system using multiple inheritance, where the corresponding class inherits from more than one parent class.

oo::class create CashManagementAccount {
    superclass CheckingAccount BrokerageAccount
}
Caution Be careful when using multiple superclass statements as the earlier declarations are overwritten by default. You need to specify the -append option in that case. See the Tcl documentation for the oo::define command for details.

Our CMA account can do it all.

% CashManagementAccount create cma CMA-00000001
→ Reading account data for CMA-00000001 from database
  ::cma
% cma cash_check Payee 500
→ Writing a check to Payee for 500
% cma buy GOOG 100
→ Buy high

Use of multiple inheritance is a somewhat controversial topic in OO circles. Be as it may, TclOO offers the facility, and also an alternative using mixins, and leaves the design choices for programmers to make. As it should be.

5. Specializing objects

The next thing we talk about, object specialization, may be new to readers who are more familiar with class-based OO languages such as C++ where methods associated with objects are exactly those that are defined for the class(es) to which the object belongs.

In TclOO on the other hand, we can further “specialize” an individual object by overriding, hiding, and deleting methods defined in the class or even adding new ones. In fact, the potential specialization includes features such as forwarding, filters and mix-ins but we leave them for now as we have not discussed them as yet. As we will see, we can even change an object’s class.

Specialization is done through the oo::objdefine command which is analogous to the oo::define command for classes except that it takes an object instead of a class. Most of the special commands like method and variable that we have seen used inside oo::define scripts can also be used inside the script passed to oo::objdefine.

5.1. Object-specific methods

Let us illustrate with our banking example. Imagine our banking system had the requirement that individual accounts can be frozen based on an order from the tax authorities. We need to define a procedure we can call to freeze an account so all transactions on the account will be denied. Correspondingly, we need a way to unfreeze an frozen account. The following code accomplishes this.

proc freeze {account_obj} {
    oo::objdefine $account_obj {
        method UpdateBalance {args} {
            error "Account is frozen. Don't mess with the IRS, dude!"
        }
        method unfreeze {} {
            oo::objdefine [self] { deletemethod UpdateBalance unfreeze }
        }
    }
}

When the freeze procedure is passed an Account object, it uses oo::objdefine to override the UpdateBalance method that was part of the object’s class definition with a object specific UpdateBalance method that raises an error instead.

It then defines a new method unfreeze that can be called on the object at the appropriate time to restore things back to normal. We could have actually defined an unfreeze procedure instead of a unfreeze method as follows:

proc unfreeze {account_obj} {
    oo::objdefine $account_obj {deletemethod UpdateBalance}
}

This would have accomplished the same job in a clearer manner. We chose to implement an unfreeze method instead to illustrate that we can actually change an object’s definition even from within the object.

There are a couple of points that need to be elaborated:

  • The self command is only usable within a method and returns the name of the current object when called without parameters. Thus the oo::objdefine command is instructed to modify the object itself. We will see other uses of the self command later.

  • Although not required in our example, it should be noted that variables defined in the class are not automatically visible in object-specific methods. They need to be brought into scope with the my variable statement.

  • When called from within a oo::objdefine script, the deletemethod erases the specified object-specific methods. It does not affect methods defined in the class so the original UpdateBalance will still be in place and will no longer be overridden.

Let us see how all this works. At present Mr. Smith can withdraw money freely from his account.

% smith_account withdraw 100
→ 999900

So far so good. Now we get a court order to freeze Mr. Smith’s account.

% freeze smith_account

Mr. Smith tries to withdraw money and run away to the Bahamas.

% smith_account withdraw [smith_account balance]
→ Account is frozen. Don't mess with the IRS, dude!

Have we affected other customers?

% $acct withdraw 100
→ 4900

No, only the smith_account object was impacted.

Cornered Mr. Smith pays up to unfreeze the account.

% smith_account unfreeze
% smith_account withdraw 100
→ 999800
Tip Notice that the class definition of UpdateBalance was not lost in the process of adding and deleting the object-specific method.

This ability to define object-specific methods can be very useful. Imagine writing a computer game where the characters are modeled as objects. Several characteristics of the objects, such as the physics determining movement, are common and can be encapsulated with a class definition. The special “powers” of each character cannot be part of this class and defining a separate class for each character is tedious overkill. The special power of a character can instead be added to the character’s object as a object-specific method. Even modeling scenarios like temporary loss of a power without a whole lot of conditionals and bookkeeping becomes very simple using the object specialization mechanisms.

5.2. Changing an object’s class

Being a true dynamic OO language, TclOO can even change the class of an object through oo::objdefine. For example, one might change a savings account to a checking account.

% set acct [SavingsAccount new C-12345678]
→ Reading account data for C-12345678 from database
  ::oo::Obj133
% $acct monthly_update
→ 0

So far so good. Let us attempt to cash a check.

% $acct cash_check Payee 100
→ unknown method "cash_check": must be balance, deposit, destroy, monthly_updat...

Naturally that fails because it is not a checking account. Not a problem, we can fix that by morphing the object to a CheckingAccount.

% oo::objdefine $acct class CheckingAccount

We can now cash checks successfully

% $acct cash_check Payee 100
→ Writing a check to Payee for 100

but monthly updates no longer work as the account is no longer a SavingsAccount.

% $acct monthly_update
→ unknown method "monthly_update": must be balance, cash_check, deposit, destro...
% $acct destroy
→ ::oo::Obj133 saving account data to database

Needless to say, you have to be careful when “morphing” objects in this fashion since data members may differ between the two classes.

Tip Note the optional form of the oo::objdefine command that we have used in the above code fragment. When the script passed to oo::define or oo::objdefine contains only one command, it can be directly specified as additional arguments to oo::define or oo::objdefine.

Lifecycle Object Generators describes an example of when such morphing might be used. Consider a state machine where each state is represented by a class that implements the state’s behaviour. When a state change occurs, the state machine object changes its class to the class corresponding to the target state. See the abovementioned reference for implementation details.

6. Using Mixins

Earlier we looked at the use of inheritance to extend a class. We will now look at another mechanism to extend or change the behaviour of classes (and objects) - mixins.

The literature on the subject describes mix-ins in several different ways, often depending on language-specific capabilities. From this author’s perspective, a mix-in is a way to package a bundle of related functionality such that it can be used to extend one or more classes or objects. In some languages, multiple inheritance is used for this purpose but we will postpone that discussion until after we have seen an example of a mix-in.

Let us go back to our banking model. Imagine we have an Electronic Fund Transfer (EFT) facility that provides for transferring funds to other accounts. We will not worry about how this is done but just assume some global procedures are available for the purpose. This facility is available to all savings accounts but only to selected checking accounts. There are several ways we could implement this but our preference in this case is for mix-ins over the alternatives for reasons we discuss later.

In TclOO a mix-in is also defined as a class in exactly the same manner as we have seen earlier. In fact, in theory any class can be a mix-in. What sets a mix-in apart is the conceptual model and how the class is used. In our example, the EFT facility would be modeled as a class that implements two methods, transfer_in and transfer_out. Conceptually, the class does not represent an object, but rather a capability or, as is termed in some literature, a role. It adds functionality to a “real” object.

oo::class create EFT {
    method transfer_in {from_account amount} {
        puts "Pretending $amount received from $from_account"
        my deposit $amount
    }
    method transfer_out {to_account amount} {
        my withdraw $amount
        puts "Pretending $amount sent to $to_account"
    }
}

Since we want all checking accounts to have this facility, we will add EFT to the CheckingAccount class as a mixin.

% oo::define CheckingAccount {mixin EFT}
% checking transfer_out 0-12345678 100
→ Pretending 100 sent to 0-12345678
% checking balance
→ 999400

We are now able to do electronic transfers on all checking accounts.

Tip Note that modifying the class definition in any manner, in this case adding a mixin, also impacts existing objects of that class. Thus the checking object automatically supports the new functionality.

In the case of savings accounts, we only want select accounts to have this facility. Assuming our savings object represents one of these privileged accounts, we can add the mixin to just that object through oo::objdefine.

% oo::objdefine savings {mixin EFT}
% savings transfer_in 0-12345678 100
→ Pretending 100 received from 0-12345678
  1003090.0
% savings balance
→ 1003090.0

Notice that the EFT class does not really know anything about accounts. It encapsulates features that can be added to any class or object that defines the methods deposit and withdraw required to support the mixin’s functionality. So if we had a BrokerageAccount class or object, we could mix it in there as well.

6.1. Using multiple mixins

A class or object may have multiple classes mixed in. So for example if we had a facility for electronic bill presentment implemented as a mixin class BillPay, we could have added it along with EFT as a mixin in a single statement

oo::define CheckingAccount {mixin BillPay EFT}

or as multiple statements

oo::define CheckingAccount {
    mixin EFT
    mixin -append BillPay
}
Caution Note the use of -append above. By default, the mixin command overwrites existing mixin configuration so without the -append option when using multiple mixin statments, only class BillPay would be mixed into CheckingAccount.

6.2. Mixins versus inheritance

Because one of its goal is to provide the required infrastructure for additional OO system to be built on top, TclOO offers a wide variety of capabilities that sometimes overlap in their effect. The question then arises as to how to choose the appropriate feature for a particular design requirement. One of these design choices involves mixins and inheritance.

We offer the author’s thoughts on the matter. Luckily, these tend to be few and far between so a couple of paragraphs is sufficient for this purpose.

Instead of mixing our EFT class into CheckingAccount, we could have made it a superclass and used multiple inheritance instead. Or even modified or derived from the CheckingAccount class to add transfer methods. Why did we choose to go the mixin route?

Not directly inheriting or modifying the CheckingAccount class was a no-brainer for obvious reasons. The functionality is something that could be used for other account types as well and it does not make sense to duplicate code and add it to every class that needs those features. That leaves the question of multiple inheritance.

There were several considerations:

  • Inheritance implies an is-a relationship between classes. Saying a checking account is-a “account that has transfer features” sounds somewhat contrived.

  • The above stems from the fact that EFT does not really reflect a real object. It is more like a set of features or capabilities that accounts have. In the real world, it would be a checkbox on a account opening form for a checking account. The general thinking is that such classes are better modeled as mixins.

  • Perhaps most important, when implemented as a mixin, we can provide the feature sets to individual accounts, for example to specific savings accounts. You cannot use multiple inheritance to specialize individual objects in this manner.

For these reasons, mixins seemed a better choice in our design (aside from the fact that we needed some example to illustrate mixins).

There is one practical aspect of TclOO design that may drive your decision. Methods implemented via mix-ins appear in the method chain before methods defined on the object whereas inherited methods appear after. This was not relevant to our example because the mix-in only added new methods. It did not override exising ones.

7. Filter methods

Imagine Mr. Smith is suspected of being up to his old tricks again and we need to monitor his accounts and log all activity. How would we do this ? We could specialize every method for his accounts via oo::objdefine and log the activity before invoking the original method. We would have to do this for every method available to the object - those defined in the object, its class (and superclasses), object mixins and class mixins. This would be tedious and error prone. Moreover, since Tcl is a dynamic language, we would have to make sure we do that any time new methods were defined for the object or any ancestor and mixin.

Filter methods offer a easier solution. A filter method is defined in the same manner as any method in the class or object. It is marked as a filter method using the filter command. Any method invocation on the object will then result in the filter method being invoked first.

We can add a filter method to the account object whose activity we want to track.

oo::objdefine smith_account {
    method Log args {
        my variable AccountNumber
        puts "Log([info level]): $AccountNumber [self target]: $args"
        return [next {*}$args]
    }
    filter Log
}

Now all actions on the account will be logged.

% smith_account deposit 100
→ Log(1): 2-71828182 ::Account deposit: 100
  Log(2): 2-71828182 ::Account UpdateBalance: 100
  999900

Notice from the output that all method invocations, even those called internally from deposit are recursively logged. The filter method must be aware that it may be recursively entered. We use the info level command to show the stack level. When methods are chained, the filter is called for every method in the chain.

Some additional notes on filter methods:

  • Many times, the filter method needs to know what method the caller is actually trying to invoke. The self target command is useful for this purpose.

  • Multiple filters may be present and are chained like any other method.

  • Because filter methods are called for all method invocations, they are generally defined with a variable number of arguments.

  • Filter methods may be defined on an object, as in our example, or on a class, in which case they will affect all objects belonging to the class.

  • Our filter method is not exported because it starts with an upper-case letter. This means it will not be called accidentally by clients of the object. However, there is no requirement that filter methods must be private.

  • The filter method normally passes on the call to the target method via next which can be called at any point in the filter method. Moreover, the filter is not required to call the target method at all.

  • The filter method may choose to pass on the target method result, some transformed version of it, or something else entirely.

  • Filter methods are bypassed when invoking constructors, destructors or the unknown method of a class.

7.1. Defining a filter class

The filter declaration need not occur in the same class that defines the filter method. This means you can define a generic class for a filter which can be mixed into a “client” class or object which can install or remove the filter at appropriate times as desired.

Let us rework our previous example. To start with a clean slate, let us get rid of the Log method we defined on the object earlier.

% oo::objdefine smith_account {
    filter -clear 1
    deletemethod Log
}
1 Note -clear option to clear any currently defined filters

Then we define a class that does the logging.

% oo::class create Logger {
    method Log args {
        my variable AccountNumber
        puts "Log([info level]): $AccountNumber [self target]: $args"
        return [next {*}$args]
    }
}
→ ::Logger

Since we only want transactions for that account to be logged, we then mix it into the object and add the filter declaration.

% oo::objdefine smith_account {
    mixin Logger
    filter Log
}
% smith_account withdraw 500
→ Log(1): 2-71828182 ::Account withdraw: 500
  Log(2): 2-71828182 ::Account UpdateBalance: -500
  999400

As you can see, we have the same behaviour as before. The advantage of course is that defining a class allows a collection of additional behaviours to be abstracted and easily added to any class or object without repeating the code.

7.2. When to use filters

Filters could be replaced by other techniques such as overriding and then chaining methods. Conversely, method overrides, such as in our account freeze example, could be replaced by filters. Usually though it is clear which one makes the most sense. Some general rules are

  • If we need to hook into multiple methods, it is easiest to use a filter method rather than override individual methods. If necessary, self target can be used within the filter to selectively hook specific methods as shown in Introspecting filter contexts.

  • When a method behaves more as an “observer” on an object as opposed to being a core part of the object’s function, a filter method is a better fit.

  • Filter methods are always placed at the front of the method chain so that can be a factor as well in deciding to use a filter.

8. Method chains

Throughout this chapter we have seen that when a method is invoked on an object, the code implementing the method for that object may come from several different places - the object, its class or an ancestor, a mixin, forwarded methods, filters or even unknown method handlers. TclOO locates the code to be run by searching the potential implementations in a specific order. It then runs the first implementation in this list. That implementation may choose to chain to the next implementation in the list using the next command and so on through the list.

8.1. Method chain order

For the exact search order and construction of this method chain, see the reference documentation of the next command. Here we will simply illustrate with an example where we define a class hierarchy with multiple inheritance, mixins, filters and object-specific methods. Note our method definitions are empty because we are not actually going to call them.

oo::class create ClassMixin { method m {} {} }
oo::class create ObjectMixin { method m {} {} }
oo::class create Base {
    mixin ClassMixin
    method m {} {}
    method classfilter {} {}
    filter classfilter
    method unknown args {}
}
oo::class create SecondBase { method m {} {} }
oo::class create Derived {
    superclass Base SecondBase
    method m {} {}
}
Derived create o
oo::objdefine o {
    mixin ObjectMixin
    method m {} {}
    method objectfilter {} {}
    filter objectfilter
}

We have created an object of class Derived that inherits from two parent classes, all of which define a method m. Further we have mixins for both a class and directly into the object. To confuse matters further, we have filters defined at both the class and object levels.

What will the method chain for method m look like? Luckily, we do not have to work it out while reading the manpage. We can do it through introspection via the info object call command.

% print_list [info object call o m]
→ filter objectfilter object method
  filter classfilter ::Base method
  method m ::ObjectMixin method
  method m ::ClassMixin method
  method m object method
  method m ::Derived method
  method m ::Base method
  method m ::SecondBase method

The output shows the method chain so we can see for example that the filter methods are first in line.

The info object call command returns a list that contains the method chain for a particular method invocation for a particular object. Each element of the list is a sublist with four items:

  • the type which may be method for normal methods, filter for filter methods or unknown if the method was invoked through the unknown facility

  • the name of the method which, as noted from the output, may not be the same as the name used in the invocation

  • the source of the method, for example, a class name where the method is defined

  • the implementation type of the method which may be method or forward

We reiterate that not every method in the chain is automatically invoked. Whether a method occuring in the list is actually called or not will depend on preceding methods passing on the invocation via the next command.

8.2. Method chain for unknown methods

What does the method chain look like for a method that is not defined for the object? We can find out the same way.

% print_list [info object call o nosuchmethod]
→ filter objectfilter object method
  filter classfilter ::Base method
  unknown unknown ::Base method
  unknown unknown ::oo::object {core method: "unknown"}

As expected, the unknown methods, where defined, are called. Note the root oo::object object which is the ancestor of all TclOO objects, has a predefined unknown method.

8.3. Retrieving the method chain for a class

The above example showed the method chain for an object. There is also a info class call command that works with classes instead of objects.

% print_list [info class call Derived m]
→ filter classfilter ::Base method
  method m ::ClassMixin method
  method m ::Derived method
  method m ::Base method
  method m ::SecondBase method

8.4. Inspecting method chains within method contexts

Within a method context, the command self call returns more or less the same information for the current object as info object call.

In addition, you can use self call from within a method context to locate the current method in the method chain. This command returns a pair, the first element of which is the same as the method chain list as returned by info class call command. The second element is the index of the current method in that list.

An example will make this clearer.

% catch {Base destroy} 1
→ 0
% oo::class create Base {
    constructor {} {puts [self call]}
    method m {} {puts [self call]}
}
→ ::Base
% oo::class create Derived {
    superclass Base
    constructor {} {puts [self call]; next}
    method m {} {
        puts [self call]; next
    }
}
→ ::Derived
% Derived create o
→ {{method <constructor> ::Derived method} {method <constructor> ::Base method}} 0
  {{method <constructor> ::Derived method} {method <constructor> ::Base method}} 1
  ::o
% o m
→ {{method m ::Derived method} {method m ::Base method}} 0
  {{method m ::Derived method} {method m ::Base method}} 1
1 Clean up any previous definitions

Note the special form <constructor> for constructors. Destructors similarly have the form <destructor>.

Tip Constructor and destructor method chains are only available through self call, not through info class call.

8.5. Looking up the next method in a chain

At times a method implementation may wish to know if it is the last method in a method chain and if not, what method implementation will be invoked next. This information can be obtained with the self next command from within a method context.

We illustrate by modifying the m method of the Derived class that we just defined.

% oo::define Derived {
    method m {} { puts "Next method in chain is [self next]" }
}
% o m
→ Next method in chain is ::Base m

As seen, self next returns a pair containing the class or object implementing the next method in the method chain and the name of the method (which may be <constructor> and <destructor>). In the case the current method is the last in the chain, an empty list is returned.

Notice that although the next method in the method chain is printed out, it does not actually get invoked because the m method in Derived no longer calls next.

Caution Do not confuse self next with next. The latter invokes the next method in the method chain while the former only tell you what the next method is.

There is one important issue solved by self next that we will illustrate with an example. Imagine we want to package some functionality as a mixin class. The actual functionality is immaterial but it is intended to be fairly general purpose (for example, logging or tracing) and mixable into any class.

% oo::class create GeneralPurposeMixin {
    constructor args {
        puts "Initializing GeneralPurposeMixin";
        next {*}$args
    }
}
→ ::GeneralPurposeMixin
% oo::class create MixerA {
    mixin GeneralPurposeMixin
    constructor {} {puts "Initializing MixerA"}
}
→ ::MixerA
% MixerA create mixa
→ Initializing GeneralPurposeMixin
  Initializing MixerA
  ::mixa

So far so good. Now let us define another class that also uses the mixin.

% oo::class create MixerB {mixin GeneralPurposeMixin}
→ ::MixerB
% MixerB create mixb
→ Initializing GeneralPurposeMixin
  no next constructor implementation

Oops. What happened? If it is not clear from the error message, the issue is that the GeneralPurposeMixin class naturally calls next so that class that mixes it in can get initialized through its constructor. The error is raised because class MixerB does not have constructor so there is no “next” method (constructor) to call.

This is where self next can help. Let us redefine the constructor for GeneralPurposeMixin.

% oo::define GeneralPurposeMixin {
    constructor args {
        puts "Initialize GeneralPurposeMixin";
        if {[llength [self next]]} {
            next {*}$args
        }
    }
}
% MixerB create mixb
→ Initialize GeneralPurposeMixin
  ::mixb

Voila! It all works now because we only call next if there is in fact a next method to call.

8.6. Controlling invocation order of methods

As we have seen in our examples, a method can use the next command to invoke its successor in the method chain. With multiple inheritance, mixins, filters involved, it may sometimes be necessary to control the order in which inherited methods are called. The next command, which goes strictly by the order in the method chain, is not suitable in this case.

The nextto command allows this control. It is similar to the next except that it takes as its first parameter the name of the class that implements the next method to be called.

nextto CLASSNAME ?args?

Here CLASSNAME must be the name of a class that implements a method appearing later in the method chain.

When might you use this ? Well, imagine you define a class that inherits from two classes whose constructors take different arguments. How do you call the base constructors from the derived class? Using next would not work because the parent class constructors do not take the same arguments.

That’s where nextto rides to the rescue as illustrated below.

oo::class create ClassWithOneArg {
    constructor {onearg} {puts "Constructing [self class] with $onearg"}
}
oo::class create ClassWithNoArgs {
    constructor {} {puts "Constructing [self class]"}
}
oo::class create DemoNextto {
    superclass ClassWithNoArgs ClassWithOneArg
    constructor {onearg} {
        nextto ClassWithOneArg $onearg
        nextto ClassWithNoArgs
        puts "[self class] successfully constructed"
    }
}

We can now call it without conflicts.

% [DemoNextto new "a single argument"] destroy
→ Constructing ::ClassWithOneArg with a single argument
  Constructing ::ClassWithNoArgs
  ::DemoNextto successfully constructed

9. Introspection

There are three things extremely hard: steel, a diamond, and to know one’s self.
— Benjamin Franklin

Luckily for us, the last part does not hold for TclOO. Just like the rest of Tcl, TclOO offers deep and comprehensive introspection capabilities. These are useful in programming dynamic object systems, runtime debugging and tracing, and building layered OO systems.

Introspection of classes and objects from any context can be done through the info class and info object ensemble commands. These have subcommands that return different pieces of information about a class or an object. In addition, the self command can be used for introspection of an object from inside a method context for that object.

9.1. Enumerating objects

The info class instances command returns a list of objects belonging to the specified class.

% info class instances Account
→ ::oo::Obj122 ::smith_account
% info class instances SavingsAccount
→ ::savings

As seen above, this command will only return objects that directly belong to the specified class, not if the class membership is inherited.

You can optionally specify a pattern argument in which case only objects whose names match the pattern using the rules of the string match command are returned. This can be useful for example when namespaces are used to segregate objects.

% info class instances Account ::oo::*
→ ::oo::Obj122

9.2. Enumerating classes

Classes are also objects in TclOO and therefore the same command used to enumerate objects can be used to enumerate classes.

% info class instances oo::class
→ ::oo::object ::oo::class ::oo::Slot ::twapi::INullProxy ::twapi::IUnknownProx...

We pass oo::class to the command because that is the class that all classes (or class objects, if you prefer) belong to. The returned list contains two interesting elements:

  • oo::class is returned because as we said it is a class itself (the class that all class objects belong to) and is therefore an instance of itself.

  • If that were not confusing enough, oo::object is also returned. This is the root class of the object hierarchy and hence is an ancestor of oo::class. At the same time it is a class and hence must be an instance of oo::class as well.

This circular and self-referential relationship between oo::object and oo::class seems strange but it is what allows all programming constructs in TclOO to be work in consistent fashion. It is also a common characteristic of many OO systems.

As before, we can also restrict the classes returned by specifying a pattern that the name must match.

% info class instances oo::class *Mixin
→ ::ClassMixin ::ObjectMixin ::GeneralPurposeMixin

9.3. Introspecting class relationships

The info class superclasses command returns the direct superclasses of a class.

% info class superclasses CashManagementAccount
→ ::CheckingAccount ::BrokerageAccount
% info class superclasses ::oo::class
→ ::oo::object

Notice that oo::object is a superclass of oo::class.

Conversely, the info class subclasses will return the classes directly inheriting from the specified class.

% info class subclasses Account
→ ::SavingsAccount ::CheckingAccount ::BrokerageAccount

As one might expect, there is also a command, info class mixins for listing mixins.

% info class mixin CheckingAccount
→ ::EFT

9.4. Checking class membership

You can get the class an object belongs to with info object class.

% info object class savings
→ ::SavingsAccount

The same command will also let you check whether the object belongs to a class, taking inheritance into account.

% info object class savings SavingsAccount
→ 1
% info object class savings Account
→ 1
% info object class savings CheckingAccount
→ 0

For enumerating the classes mixed-in with an object, use info object mixins, analogous to info class mixins.

% info object mixins savings
→ ::EFT

From within the method context of an object, the command self class command returns the class defining the currently executing method. Note this is not the same as the class the object belongs to as the example below shows.

% catch {Base destroy} 1
→ 0
% oo::class create Base {
    method m {} {
        puts "Object class: [info object class [self object]]"
        puts "Method class: [self class]"
    }
}
→ ::Base
% oo::class create Derived { superclass Base }
→ ::Derived
% Derived create o
→ ::o
% o m
→ Object class: ::Derived
  Method class: ::Base
1 Clean up any previous definitions
Caution The self class command will fail when called from a method defined directly on an object since there is no class associated with the method in that case.

9.5. Enumerating methods

The list of methods implemented by a class or object can be retrieved through info class methods and info object methods respectively. Options can be specified to control whether the list includes inherited and private methods.

% info class methods CheckingAccount 1
→ cash_check
% info class methods CheckingAccount -private 2
→ cash_check
% info class methods CheckingAccount -all 3
→ balance cash_check deposit destroy transfer_in transfer_out withdraw
% info class methods CheckingAccount -all -private 4
→ <cloned> UpdateBalance balance cash_check deposit destroy eval transfer_in tr...
% info object methods smith_account -private 5
1 Lists all methods defined and exported by CheckingAccount itself
2 Lists both exported and private methods defined by CheckingAccount
3 Lists all methods defined and exported by CheckingAccount, its ancestors, or mixins
4 Lists both exported and private methods defined by CheckingAccount, its ancestors, or mixins
5 Lists exported and non-exported methods defined in the object itself

The list of methods that are set as filters can similarly be obtained with info class filters or info object filters.

% info object filters smith_account
→ Log

9.6. Retrieving method definitions

To retrieve the definition of a specific method, use info class definition. This returns a pair consisting of the method’s arguments and its body.

% info class definition Account UpdateBalance
→ change {
          set Balance [+ $Balance $change]
          return $Balance
      }

The method whose definition is being retrieved has to be defined in the specified class, not in an ancestor or a class that is mixed into the specified class.

Constructors and destructors are retrieved differently via info class constructor and info class destructor respectively.

% info class constructor Account
→ account_no {
          puts "Reading account data for $account_no from database"
          set AccountNumber $account_no
          set Balance 1000000
      }

9.7. Retrieving the method chain

The info class call command retrieves the method chain for a method. From a method context, the self call command returns similar information while self next identifies the next method implementation in the chain.

We have already discussed all these in detail in an earlier section.

9.8. Introspecting filter contexts

When a method is run as a filter, it is often useful for it to know the real target method being invoked. This information is returned by self target which can only be used from within a filter context. Its return value is a pair containing the declarer of the method and the target method name.

For example, suppose instead of logging every transaction as in our earlier example, we only wanted to log withdrawals. In that case we could have defined the Log command as follows:

oo::define Logger {
    method Log args {
        if {[lindex [self target] 1] eq "withdraw"} {
            my variable AccountNumber
            puts "Log([info level]): $AccountNumber [self target]: $args"
        }
        return [next {*}$args]
    }
}

We would now expect only withdrawals to be logged.

% smith_account deposit 100
→ 999500
% smith_account withdraw 100
→ Log(1): 2-71828182 ::Account withdraw: 100
  999400

We are 100% right. Again!

The other piece of information that is provided inside a filter method is about the filter itself and is available through the self filter command.

Let us redefine our Log filter yet again to see this.

% oo::define Logger {
    method Log args {
        puts [self filter]
        return [next {*}$args]
    }
}
% smith_account withdraw 1000
→ ::smith_account object Log
  ::smith_account object Log
  998400

As seen above, the self filter command returns a list of three items:

  • the name of the class or object where the filter is declared. Note this is not necessarily the same as the class in which the filter method is defined. Thus above, the filter was defined in the Logger class but declared in the smith_account object.

  • either object or class depending on whether the filter was declared inside an object or a class.

  • the name of the filter.

Note You will see two output lines in the above example. Remember the filter is called at every method invocation. Thus the Log method is invoked twice, once before the withdraw method, and then again when that method in turn calls UpdateBalance.

9.9. Object identity

Who in the world am I? Ah, that’s the great puzzle.
— Lewis Carroll
Alice in Wonderland

Under some circumstances, an object needs to discover its own identity from within its own method context.

An object can be identified in two ways:

  • the command name used to invoke the object methods

  • the unique namespace in which the object state is stored

The former is returned by the self object command, which can also be called as simply self. A method may need to know command name of the object

  • when an object method has to be passed to a callback

  • when an object is redefined “on the fly” from within an method, its name must be passed to oo::objdefine.

See Object-specific methods for an example.

The unique namespace associated with the object is obtained through the self namespace command within a method context or with the info object namespace command elsewhere.

% oo::define Account {method get_ns {} {return [self namespace]}}
% savings get_ns
→ ::oo::Obj128
% set acct [Account new 0-0000000]
→ Reading account data for 0-0000000 from database
  ::oo::Obj158
% $acct get_ns
→ ::oo::Obj158
% info object namespace $acct
→ ::oo::Obj158

Notice when we create an object using new the namespace matches the object command name. This is an artifact of the implementation and this should not be relied on. In fact, like any other Tcl command, the object command can be renamed.

% rename $acct temp_account
% temp_account get_ns
→ ::oo::Obj158

As you can see, the object command and its namespace name no longer match. Also note the namespace does not change when the command is renamed.

9.10. Enumerating data members

The command info class variables returns the list of variables that have been declared with the variable statement inside a class definition and are therefore automatically brought within the scope of the class’s methods.

% info class variables SavingsAccount
→ MaxPerMonthWithdrawals WithdrawalsThisMonth

The listed variables are only those defined through the variable statement for that specified class. Thus the above command will not show the variable Balance as that was defined in the base class Account, not in SavingsAccount.

For enumerating variables for an object as opposed to a class, there are two commands:

  • info object variables behaves like info class variables but returns variables declared with variable inside object definitions created with oo::objdefine. These may not even exist yet if they have not been initialized.

  • info object vars returns variables currently existing in the object’s namespace and without any consideration as to how they were defined.

Hence the difference between the output of the following two commands.

% info object variables smith_account
% info object vars smith_account
→ Balance AccountNumber

The first command returns an empty list because no variables were declared through variable for that object. The second command returns the variables in the object’s namespace. The fact that they were defined through a class-level declaration is irrelevant.

10. References

DKF2005

TIP #257: Object Orientation for Tcl, Fellows et al, http://tip.tcl.tk/257. The Tcl Implementation Proposal describing TclOO.

WOODS2012

Lifecycle Object Generators, Woods, 19th Annual Tcl Developer’s Conference, Nov 12-14, 2012. Describes a number of useful OO patterns built on top of TclOO.


1. No value judgement intended