Updated 2014-06-07 17:31:23 by dkf

DKF: Recently, as part of work on TIP #257: Object Orientation for Tcl, I discovered that I needed to know the exact order that XOTcl methods, mixins, filters, etc. are traversed, especially when in combination with each other. The general descriptions in the language reference[1] and tutorial[2] were clear enough, especially in the case that a method theoretically appears twice in the traversal order (in fact, it appears in the last place it can), but the cases where the various aspects combine were far less clear. So I found a starkit containing XOTcl 1.3.2 (thanks to the XOTclIDE folks!) and ran the script below to find out what was actually going on!
 package require XOTcl

 Class create Amix
 Amix instproc foo {} {lappend ::result [self class]; next}

 Class create Bmix
 Bmix instproc foo {} {lappend ::result [self class]; next}
 Bmix instproc barBmix args {lappend ::result [self filterreg]; next}
 Bmix instfilter barBmix

 Class create Cmix
 Cmix instproc foo {} {lappend ::result [self class]; next}

 Class create Dmix
 Dmix instproc foo {} {lappend ::result [self class]; next}

 Class create objmix
 objmix instproc foo {} {lappend ::result [self class]; next}

 Class create A -instmixin Amix
 A instproc foo {} {lappend ::result [self class]; next}
 A instproc barA args {lappend ::result [self filterreg]; next}
 A instproc barB args {lappend ::result [self filterreg]; next}
 A instproc barC args {lappend ::result [self filterreg]; next}
 A instproc barD args {lappend ::result [self filterreg]; next}
 A instfilter barA

 Class create B -superclass A -instmixin Bmix -instfilter barB
 B instproc foo {} {lappend ::result [self class]; next}

 Class create C -superclass A -instmixin Cmix -instfilter barC
 C instproc foo {} {lappend ::result [self class]; next}

 Class create D -superclass {B C} -instmixin Dmix -instfilter barD
 D instproc foo {} {lappend ::result [self class]; next}

 D create obj -mixin objmix
 obj proc foo {} {lappend ::result obj; next}
 obj proc barobj {args} {lappend ::result [self filterreg]; next}
 obj filter barobj

 set ::result {}
 obj foo
 puts [join $::result \n]

To read this, understand that there is one "object" created called obj, which is of class D. Above D there are three classes, A, B and C, in a classic diamond shape. Attached to each class there is a mixin class attached, e.g. Amix attaches to A. There is also another mixin class, objmix, that is attached specifically to the object. The mixin classes are not related to each other; that would just muddy things further!

Each of these classes and mixin classes has a method, foo, that just adds the identity of the class to a global accumulator variable and hands off to the next method in the method traversal order.

In addition, the A class, the Bmix class and the obj object have methods (their names starting with bar) that are are installed on various classes and the obj object as filters. The filters append some filter-related information (a three-element list) to the result before passing on.

Once all that's set up, the script just initializes the accumulator, runs through the method chain (invoking every defined method in the process, as it happens), and prints the result out. The result from the script is as follows; note that there is a leading blank line, this is significant (and is indicative of a bug in this version of xotcl) as it comes from the one filter that appears to otherwise not be called:
 ::obj filter barobj
 ::D instfilter barD
 ::B instfilter barB
 ::C instfilter barC
 ::A instfilter barA

What does this mean? It means that all filters (including filters due to mixin traversal) run before all other methods. It means that all mixin traversal for ordinary methods happens after that, before the object implementation, and finally the class hierarchy traversal. It also means that filter traversal is done in the same order that the traversal for the subsequent ordinary method part of the method call chain is done.

Other experiments (not shown here) indicate that calling a filter method directly causes truncation of the chain in the tested version of xotcl. This is probably a bug.

Donal Fellows

NEM I emailed the XOTcl mailing list about this a few days ago. My take from reading the docs was that the order is: filters -> mixins -> object/class methods -> "unknown" mechanism, and that for each of these the lookup is done in order: object instance -> class -> ?superclass ...?, so e.g. per-object mixins are searched before per-class mixins (and then superclass mixins). Gustaf replied with a more detailed explanation [3], but the general outline seems correct. This also seems to agree with your findings.