iGloo: A Problem with Delayed Creation

Last week I looked into the problem of creating megawidgets using TIP 257‘s oo:: framework, and concluded that it was possible to hijack the Tk widget command in the class’s create method.

It turns out that there is a serious problem with this technique: you can’t easily create subclasses of your new megawidget class. (To follow the rest of this discussion you should go back and read the earlier post now, if you haven’t already.)

In the example, I create a simple read-only text widget called rotext. The first step in the technique I described was to redefine the new megawidget class’s create method to do the hijacking step. As shown, we create a text widget, rename it, and then call next to get the default create method, which actually creates the object and calls its constructor:

    self.method create {win args} {
        # FIRST, create a text widget with the desired name.
        # (We could also hijack a widget that had already been
        # created.)
        text $win

        # NEXT, rename it to something else.
        set hull ::_$win
        rename $win $hull

        # NEXT, create a new object with the original name,
        # passing the new name to the constructor.  Note that
        # we add the "::" to make sure the command is created in
        # the global scope.
        next ::$win $hull {expand}$args

        # NEXT, return the bare window name
        return $win
    }

Then, in the constructor we take the renamed text widget command, and save it in an instance variable called hull so we can use it. We do this by passing the renamed command name to the constructor as an argument, because that’s really the only way the class has to communicate this name to the instance.

    constructor {theHull args} {
        my variable hull

        # FIRST, save the hull
        set hull $theHull

        # NEXT, pass the options to the hull
        $hull configure {expand}$args
    }

So what happens if we try to subclass our new rotext type? Here’s a simple subclass:

    oo::class create subrotext {
        superclass rotext

        # ....
    }

Now, watch happens when we try to create an instance of subrotext:

    % subrotext create .text -width 40 -height 10
    Error in startup script: invalid command name "-width"
        while executing
    "$hull configure {expand}$args"
        (class "::rotext" constructor line 8)
        invoked from within
    "subrotext create .text -width 40 ..."
       ....

What happened here? What happened is that subrotext inherited the constructor from rotext, but not the rotext class method create. Thus, we got the default create method, which does no hijacking, and doesn’t pass the new hull command name to the constructor.

In short, every subclass of rotext would need to explicitly redefine its create class method to do the hijacking. So much for reuseable code.

This is rather a poser, and I’ve not figured out my way around it yet. Should class methods be inherited by subclasses? If not, why not?

Update 11/10/2006: Aha! Figured it out. My mistake, above, was using oo::class to create the subclass of rotext. I should have defined a new metaclass, called igloo::widgetadaptor or something of the sort, that guarantees the right create semantics, and created the subrotext as follows:

    igloo::widgetadaptor create subrotext {
        superclass rotext
        # ....
    }

Here, grasshopper, is a deep and abiding truth: a class gets its instance methods from itself and its superclasses and mixins, but it gets its class methods from itself and from the metaclass used to create it. Write that on your forehead in permanent ink.

2 thoughts on “iGloo: A Problem with Delayed Creation

  1. Snit’s stable and in use; TclOO is progressing, and does many things it didn’t do when I was working on iGloo; and iGloo is defunct. Not to say that I won’t get back to it at some point.

    Like

Comments are closed.