iGloo: Mixing in a Mixin

In OO, a “mixin” class is a class that’s intended to add a particular feature to any number of other classes via multiple inheritance. You wouldn’t generally subclass a mixin class by itself; it’s just something you add in where it’s needed.

TIP 257 supports mixins directly: for any class you can provide a list of the classes that it mixes in. The relationship isn’t the same as the superclass relationship, even if that’s how mixins are done in C++; it’s all about calling order. Here’s the difference: it’s all about the order of method chaining.

Suppose I have two classes, dog and bigdog, where bigdog is a subclass of dog. Further, both classes define a bark method:

oo::class create dog {
    ...
    method bark {} { return "Woof!"}
}

oo::class create bigdog {
    superclass dog
    ...
   method bark {} { return "WOOF!"}
} 

% bigdog create spot
::spot
% spot bark
WOOF!

What happens is that bigdog‘s bark simply replaces dog‘s bark in all bigdog objects. But suppose we wanted to use dog‘s bark; we just want to make it LOUDER. Using method chaining, we could define it this way:

oo::class create bigdog {
    superclass dog
    ...
   method bark {} { string upper [next] }
} 

The next command chains to the method of the same name in the parent class; you can pass any arguments you like. In this case, next calls dog‘s bark, which returns “Woof!”; bigdog upper cases it to “WOOF!” and returns it. Thus, the subclass can do some work, call the same method in the parent class, and then do some more work. We say that bigdog‘s bark wraps dog‘s bark.

With mixins, the order is the opposite. The main class gets all of the mixin class’s methods; and the mixed-in methods can chain to the main class’s methods. Suppose you wanted to print a trace of all method calls on a particular object. You could do that like this:

oo::class create dog_logger {
    method bark {} {
        puts "Entering bark."
        next
        puts "Exiting bark."
    }

    method wag {} {
        puts "Entering wag."
        next
        puts "Leaving wag."
    }
}

oo::class create dog {
    mixin dog_logger

    method bark {} { puts "Bark! Bark!" }
    method wag  {} { puts "Wag! Wag!" }
}

dog create spot
spot bark
spot wag

Running this script yields the following output:

Entering bark.
Bark! Bark!
Exiting bark.
Entering wag.
Wag! Wag!
Leaving wag.

Very interesting, and useful.

I do have some questions about mixins that aren’t answered in the TIP. The main one involves constructors and destructors. In my tests, the mixed-in class’s constructor never gets called; I imagine its destructor never gets called either. It would appear that if the mixin has any state that needs to be initialized, it must provide an initialization method which the main class needs to call explicitly, within its own constructor. I’m not at all sure whether I like this or not, but I think it’s probably workable. I do suspect that a class really has to be designed as a mixin if it’s to be used effectively as one.