iGloo: The “mymethod” command

The Problem

It’s common for Snit objects to register callbacks with other objects. For example, a snit::widget might create a Tk button; when the button is pressed, the callback should call one of the widget’s methods. The naive way to do this in the widget’s constructor would be this:

$button configure -command [list $self ButtonPress]

Here, $self is the name of the widget, and so the widget’s ButtonPress method will be called whenever the $button is pressed by the user.

Now, this code will work fine–right up until the moment the programmer decides to rename the widget, as might happen if the programmer applies a snit::widgetadaptor to it. Consequently, Snit provides a way to build callback commands that will continue to call the correct Snit object even if the object’s name changes. The mechanism is the mymethod command:

$button configure -command [mymethod ButtonPress]

mymethod takes the method name, and any additional arguments the programmer cares to provide, and returns a command that’s insensitive to name changes. I won’t go into how the trick is done; it’s ugly (and, by the way, thanks to Andreas Kupries for getting it working right). The question is, what’s the easiest way to do this in the TIP 257 environment?

The “my” Command

In Snit, an object calls its methods via its $self variable, which contains the current name of the object. TIP 257 provides the my command instead. my can be thought of as an alias to the object’s command, an alias that’s always available and never changes its name. That’s not completely true, but it’s good enough for now. Now, every object has a Tcl namespace associated with it; and it happens that my appears as a command in that namespace. By fully qualifying my with its namespace, we gain a command that references the object, whose name won’t ever be changed, and that can be called from outside code. Thus, the following code would do the trick:

$button configure \
    -command [list [self namespace]::my ButtonPress]

We can wrap this in a proc, like so; this will work just fine provided that the proc is visible to the object’s code:

proc mymethod {args} {
    linsert $args 0 [uplevel 1 [list self namespace]]::my
}

And then, there are any number of ways we can make it visible. One slightly tacky method is to put it in the ::oo::Helpers namespace; this namespace is made visible (by namespace path) to all TIP 257 objects. (I call it slightly tacky because it effects all objects, and because the existence of the ::oo::Helpers namespace isn’t part of the TIP 257 spec, at least at the moment.)

A Bonus

There’s a bonus to using this method. In Snit, there’s no real distinction between private and public methods, i.e., methods defined for use solely by the object itself for its own internal processing, and methods that form part of the object’s public interface. TIP 257, however, has the notion of “exported” and “unexported” methods; the latter can only be called using the my command, and hence are reasonably private. But since mymethod as defined above makes use of my, this means that an object’s callbacks can call the object’s unexported, i.e., private methods–which is usually exactly what you want.