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.

iGloo: A Dirty Trick

Recently I suggested that it would be interesting if TIP 257 defined a myclass command which an object could use to access its class’s unexported methods and also its class variables. I’ve figured out a remarkably tacky way to do so–it’s so tacky that I’m almost ashamed to describe it.

First, remember that myclass would simply be the class’s own my command aliased into the instance’s namespace. If the instance knew the class’s namespace it could define myclass quite simply using interp alias:

    interp alias {} [self namespace]::myclass {} ${classns}::my

Given this, the instance could then declare and use its class’s variables; this is often useful for lookup tables which are static and can be shared by all of the class’s instances:

    myclass variable lookupTable

    set value $lookupTable($key)

The question is, how can the instance acquire the class’s namespace? One way would be to define an exported class method called namespace, but that affects the class’s public interface, which we don’t want to do. So here’s a dirty trick: we temporarily export the class’s eval method, and use it to query the class’s namespace. We can do this any place in the instance code:

    # FIRST, get the class's name
    set class [info object class [self]]

    # NEXT, export its eval method, making it public.
    oo::define $class self.export eval

    # NEXT, get the namespace
    set classns [$class eval {namespace current}]

    # NEXT, unexport its eval method, making it private
    oo::define $class self.unexport eval

    # NEXT, alias in myclass
    interp alias {} [self namespace]::myclass {} ${classns}::my

As I say, I’m almost ashamed of this trick. It’s a very roundabout way to get information the class doesn’t really want me to have; and then, also, it’s rather fragile (at least when applied to classes found in the wild). I’m making a couple of unwarranted assumptions: first, that the class hasn’t redefined eval for its own purposes, so that it no longer does what I think it does, and second, that the class wants eval to be private. Both are just reasonable enough that the corner-cases will catch you unawheres.

In a controlled environment, though, this trick might be worth using. For example, an instance of an igloo::type might conceivable use this trick during its construction…if I can’t figure out an easier way to get the job done.

iGloo: Facing the Unknown

Snit allows you to delegate unknown methods to some specific component. If you’re adapting a widget, for example, you can redefine just those methods you care about, and delegate all of the remaining methods to the widget you’re adapting by adding the following to your widget definition:

    delegate method * to hull

This feature obviously requires a way to detect and handle unknown methods. Now, TIP 257 provides such a mechanism: the unknown method. Consider the following code:

    oo::class create myclass {
        method unknown {methodName args} {
            puts "Unknown Method: $methodName $args"
        }
    }

An Observation: As I’ve commented elsewhere, methods in TIP 257 can be exported or unexported. If they are exported, they can be called by users of the object; if unexported, they can only be called using the object’s my command. Now, methods whose names begin with a lower case letter are exported by default–which means that the unknown method is exported by default. For my part, I can’t think of any reason why I’d want unknown to be exported…which means that I need to remember to write it this way instead:

    oo::class create myclass {
        method unknown {methodName args} {
            puts "Unknown Method: $methodName $args"
        }
        
        # Remember to unexport it!
        unexport unknown
    }

This is a nuisance. It would be much nicer if unknown were called either Unknown or _unknown instead.

But this is by the way; my real concern here is how to do delegation of unknown methods. Currently available versions of Snit use two different methods: Snit 2.1 uses namespace ensemble‘s unknown handler, and Snit 1.2 does it the hard way.

Unknown Methods and Namespace Ensemble: When you create an ensemble command using namespace ensemble you have the option of specifying an -unknown handler. The operation of the handler is somewhat surprising: it does not, in fact, execute the method itself.

Remember that a namespace ensemble is defined by a dictionary of method names and fully-qualified commands. The -unknown handler is called whenever a method is called that doesn’t appear in the dictionary. It’s the -unknown handler’s job to come up with the fully-qualified command that corresponds to the previously unknown method. Now, here’s the trick: the -unknown handler doesn’t call this fully-qualified command; instead, it simply installs it back into the ensemble’s dictionary. The ensemble itself then makes one more attempt to call the hitherto unknown method.

This is, quite simply, brilliant, though you likely won’t see why unless you’ve tried to implement a similar scheme yourself–that is, when you’ve done it the hard way.

Method Dispatch The Hard Way: If you don’t use namespace ensemble, then dealing with unknown methods in pure-Tcl code is both easier and harder. It’s easier, because dispatching to a previously unknown method is no more difficult than dispatching to a known method–you’ve got to look up the method name anyway, to see how to dispatch to it, so unknown methods are just a special kind of lookup. It’s harder, because dispatching to any method is a royal pain in pure-Tcl, at least if you handle all of the corner cases.

Ideally, writing an object method should be just like writing a proc. A method should be able to do all of the things a proc can do, and in the same way. In particular,

  • A method should be able take any argument list supported by proc, including the use of optional arguments and args.
  • A method should be able to use upvar to access variables in its caller’s scope.
  • A method should be able to use uplevel 1 to execute code in its caller’s scope.
  • A method should be able to return -code to return unusual error codes, especially break–this is often needed when writing Tk event handlers.

If you write your own method dispatcher in pure Tcl without using namespace ensemble, you have to handle all of this yourself. Suppose, for example, I want to implement an ensemble command myobject. The methods will be implemented as procs called myobject_name. A pure-Tcl dispatcher will look something like this:

proc myobject {method args} {
    # FIRST, determine the actual command.  For this example we'll
    # skip the error checking, and simply assume that the proc that
    # implements a method is myobject_$method.

    set cmd [linsert $args 0 myobject_$method]

    # NEXT, call the command using uplevel 1; this effectively removes
    # [myobject] from the call stack, so that upvar and uplevel
    # will work properly in the method body.

    set retval [catch {uplevel 1 $cmd} result]

    # NEXT, if the return code is anything but "ok" we need to rethrow
    # it--this time, we're removing [myobject] from the call stack on
    # the return trip.
    #
    # Note that error returns need a little extra help.

    if {$retval} {
        if {$retval == 1} {
            return \
                -code error             \
                -errorinfo $::errorInfo \
                -errorcode $::errorCode \
                $result
        }
        return -code $retval $result
    }

    return $result
}

Here’s a sample method; it takes a variable name and increments the variable.

proc myobject_incr {varname} {
    upvar 1 $varname theVar
    incr theVar
}

Here’s how it looks in practice:

% set a 1
1
% myobject incr a
2
% set a
2
%

Note that it took me about an hour to write myobject and prove to myself it really handles all of the corner cases; the code and the test cases are available at http://www.wjduquette.com/igloo as rethrow.tcl. It isn’t quite perfect, either; the stack trace you get in the case of an error thrown in a method body is way ugly, and reveals too much information about how the ensemble is implemented. (This is one of Snit 1.2’s real warts.)

Back to Namespace Ensemble: The beauty of namespace ensemble is that it handles all of this for you, and it does it in C code so there’s little run-time penalty. And because of the brilliant design of the -unknown handler, it works for unknown methods too.

Unknown Methods in TIP 257: So, how does the TIP 257 unknown method stack up against namespace ensemble? The answer, unfortunately, is “badly”. You’re essentially back in the bad old days: the unknown method has to do all of the work myobject does in the example shown above. The resulting code works, but it’s ugly and slow. Since automatic delegation of unknown methods is one of Snit’s strong points, and since one of the motivations for building Snit on TIP 257 is speed, this is a serious concern.

Here’s some example TIP 257 code that shows, in classic Snit style, how to make a dog wag its tail. For simplicity, I’ve omitted code that exercises the corner cases. The full code is available as unknown1.tcl.

::oo::class create tail {
    method wag {speed} {
        if {$speed eq "fast"} {
            return "Wag, wag!"
        } elseif {$speed eq "slow"} {
            return "Wag."
        } else {
            return "Whine!"
        }
    }
}

::oo::class create dog {
    # A dog has a mytail component
    constructor {} {
        my variable mytail

        set mytail [tail new]
    }

    # Let's delegate unknown methods to mytail.
    method unknown {methodName args} {
        my variable mytail

        # FIRST, determine the actual command.
        set cmd [linsert $args 0 $mytail $methodName]

        # NEXT, call the command using uplevel 1; this effectively 
        # removes [[self] unknown] from the call stack, so that upvar 
        # and uplevel will work properly in the method body.

        set retval [catch {uplevel 1 $cmd} result]

        # NEXT, if the return code is anything but "ok" we need to 
        # rethrow it--this time, we're removing [myobject] from the 
        # call stack on the return trip.

        if {$retval} {
            if {$retval == 1} {
                return \
                    -code error             \
                    -errorinfo $::errorInfo \
                    -errorcode $::errorCode \
                    $result
            }
            return -code $retval $result
        }

        return $result
    }

    # Make unknown invisible to uses
    unexport unknown
}

Can we do better than this? Possibly. TIP 257 supports a minimal delegation mechanism that allows any method to be forwarded to any arbitrary command. The semantics are similar to those of interp alias or namespace ensemble, which is just what we’re looking for. The problem is, in order to forward an unknown method we have to know the method name–and we don’t know the method name until after unknown is called. This suggests a hybrid scheme:

  • When an unknown method is called, dispatch it “the hard way”.
  • If the call succeeds, the method is no longer unknown; forward it explicitly, so that subsequent calls do it “the easy way”.

We can do this by adding a single line of code after the [catch] statement in the unknown method shown above: (See unknown2.tcl)

    if {$retval != 1} {
        oo::define [self] self.forward $methodName  $mytail $methodName
    }

We now do things the slow way the first time only; after that, we get namespace ensemble-like goodness and speed. In my benchmarks, the hybrid version is just over twice as fast on average.

Some Warts: Both methods of delegating unknown methods have some warts, especially in the area of error returns. The wag method shown above requires one argument. Let’s look at some error messages when we implement delegation the naive way:

% source unknown1.tcl
% spot wag fast slow
wrong # args: should be "::oo::Obj6 wag speed"
% spot wag
wrong # args: should be "::oo::Obj6 wag speed"
%

Here the error message is written in terms of the tail component, ::oo::Obj6, which is seriously ugly. If we use the hybrid approach we see things like this:

% source unknown2.tcl
% spot wag fast slow
wrong # args: should be "::oo::Obj6 wag speed"
% spot wag fast
Wag, wag!
% spot wag fast slow
wrong # args: should be "spot wag"
% spot wag
wrong # args: should be "spot wag"
%

First, we get the “naive”-style error messages until the first time the method call succeeds. This is annoying. Then, once the method had been forwarded we get an error message that gets the object right but omits the argument names. One doesn’t know whether to laugh or cry. I choose to look at this as a bug in TIP 257.

Conclusions: The hybrid approach is definitely the preferred way to delegate unknown methods, given the current implementation, although the automatically generated error messages are unfortunate.

It would be spiffy if the unknown handler could do this:

    # Let's delegate unknown methods to mytail.
    method unknown {methodName args} {
        my variable mytail

        # FIRST, determine the actual command.
        set cmd [linsert $args 0 $mytail $methodName]

        # NEXT, forward it.
        oo::define [self] self.forward $methodName  $mytail $methodName

        # NEXT, ask the object to retry the call.
        return -code retry
    }

The new return code retry would tell the caller to retry the call–that is, to call it exactly one more time. This would give us similar semantics to namespace ensemble‘s unknown handler.

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.

iGloo: Type Variables and Type Methods

In Snit, you define types; types then have instances. To use a canonical instance, you define a dog type, and then create dog objects. You can define methods and instance variables; and you can define type methods and type variables. Here’s an example:

% snit::type dog {
    typevariable count 0

    typemethod count {} { return $count }

    constructor {args} {
        ...
        # Increment the number of dogs
        incr count
        ....
    }

    destructor {
        # Decrement the number of dogs
        incr count -1
    }

    ...
}
::dog
% dog count
0
% dog spot
::spot
% dog fido
::fido
% dog count
2
%

Here we write a type that keeps track of how many instances it currently has. The point is that type variables are visible without declaration in instance code (e.g., constructor, destructor, and method bodies). In addition, type methods can be called by instance code via the $type command.

Supporting this under TIP 257 is tricky. In Snit, every instance belongs to exactly one type. There’s no ambiguity as to where an instance’s type variables and type methods reside: they reside in the instance’s type.

Under TIP 257, any object has immediate access to its own methods via the my command, and to its instance variables via the my variable command. It can discover its own namespace using namespace current or self namespace, and access its variables by fully qualified paths. The one thing the object doesn’t have easy access to is the class to which it belongs; and so it’s unclear how the object can unambiguously call methods of its class or access its class’s variables.

Just what does a TIP 257 object know about its class? It can retrieve a class name using the self class command; but according to the tip, this returns “the name of the class that defines the currently executing method.” Thus, if the currently executing method was defined by a superclass of the object’s class we get the superclass name rather than the actual class name. So self class is no help.

However, it appears that info object class does the right thing. So our object could call methods on its class in this way:

    [info object class [self]] methodName

Ugh. Sure, it works, but it’s ugly. Further, it means that the object can only access its class’s exported methods, although I can think of cases where an object might want to call some of its class’s unexported methods as well–methods that implement some private resource shared by all instances of the class.

I’ll note in passing that it’s possible to change an object’s class dynamically. (This makes my brain hurt!)

So what about class variables? The only handle we’ve got is the name of the class’s own private namespace. Given the name of that namespace, we can use the class’s my variable command to bring its variables into the current scope. In fact, we can then use the class’s my command to call any of its methods, exported or unexported. And if we were to alias the class’s my command into the object’s namespace as myclass, we’d be really set.

The problem is finding out the name of the class’s namespace, because you can only do that in the class’s own code. I think what iGloo would have to do is pass the fully-qualified name of the class’s my command into each object as it is created; the object can then alias it into its own namespace as myclass. But then, if the object is given a new class we’re in trouble.

Bottom line: In my opinion a myclass command, defined as above, makes a lot of sense. But I think it needs to be defined explicitly as a part of TIP 257; otherwise, it’s too hard to handle all of the edge cases (as when an object’s class is changed).

iGloo: Delayed Creation

One of the particular problems that’s been bugging me is the issue of creating megawidgets in iGloo. A megawidget, of course, is simply an object that acts and looks like a widget; and for an object to act like a widget, it has to have a real Tk widget at its core. The process goes something like this:

First, you decide what to call your widget, remembering that there are definite constraints on what a widget can be called. For the sake of argument we’ll create a widget called .text.

Next, you create a real Tk widget with that name, to serve as the hull of your megawidget:

text .text

Next, you rename the real widget something else:

rename .text _.text

Next, you create your own command called .text. This command should pass most of its subcommands to the hull (_.text), and define any new or modified subcommands it chooses.

proc .text {method args} {
    .
    .
    .
    _.text $method {expand} $args
    .
    .
    .
}

You’ve now got a megawidget, more or less. (As always, the devil is in the details.) I call this process of replacing the widget’s command with my own command hijacking the widget command.

The question is, how can one write a TIP 257 object that does this? It’s a little bit more involved, because you already have a command name when you enter the constructor. Consider the following code snippet, which is part of some code to create a read-only text (rotext) widget:

    constructor {args} {
        my variable hull

        # FIRST, rename [self] to something else, temporarily
        set self [self]
        rename [self] ::_temp$self

        # NEXT, create the Tk widget (remove the ::)
        text [namespace tail $self] {expand}$args

        # NEXT, rename the Tk widget
        set hull ::_$self
        rename $self $hull

        # NEXT, put self back
        rename ::_temp$self $self
    }

As you can see, you need to move your chosen window name out of the way, then create the Tk widget, then move it out of the way, and then put your own command back again. This code works, but it has two problems: first, it doesn’t allow you to hijack a previously existing widget, which is sometimes handy. And second, look what happens when you create your widget:

% rotext .text
::.text
%

Whoops! When you create a TIP 257 object, you get a fully-qualified command name! But when you create a Tk widget, you get back the window name. It’s true that .text and ::.text name the same command; but the first is a window name and the second isn’t.

The problem here is that by the time we enter the constructor the object has already been created. What we’d like is the chance to do a little work before the object is created. And the way you do that is by redefining the rotext class’s create method:

    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
    }

    constructor {theHull args} {
        my variable hull

        # FIRST, save the hull
        set hull $theHull

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

First, we use self.method to define a new create method for our class. We create a text widget, and rename it. Then we use the next command to invoke the superclass’s create method; this will create the object and call its constructor. We insert the Tk widget’s new name into the argument list. The constructor then grabs the Tk widget’s new name and saves it in the instance variable hull, and passes the widget’s options to it.

It remains to redefine the insert and delete methods, and forward the other methods to the real text widget. Here’s the complete example: rotext.tcl.

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.

iGloo: A Different Kind of Snit

I recently was able to get a hold of a build of Donal Fellow’s Object-Oriented Programming extension for Tcl, familiarly known as “TIP 257“. I helped come up with the spec for TIP 257 last year, but just as no battle plan survives first contact with the enemy, so no software design spec survives implementation unchanged. Consequently I’ve been spending the last few days getting familiar with the revised spec.

The primary goal of TIP 257 is to provide a fast, minimal OOP implementation for the Tcl language which can be used directly and which can also serve as a foundation for significantly less minimal frameworks. One of those less minimal frameworks is my own Snit package, which is available as part of Tcllib. Currently, Snit is doesn’t depend on TIP 257; there are two reasons for making it do so, speed and functionality. Snit is highly optimized, but there are some things that are just slow. TIP 257 does considerably more in C, and consequently should be faster at these things. Second, as object systems go Snit provides the bare bones; for example, it doesn’t allow inheritance (which would be seriously nice to have on occasion). Snit-on-257 might really be Snit-on-steroids.

Now, I’m confident that I can build a Snit-workalike on top of Donal’s API (after all, I can build it without Donal’s API). But there are a number of questions whose answers I need to discover. What compromises and workarounds am I going to have to make to duplicate Snit’s precise syntax and semantics? If I end up with something that has no additional speed and no additional features, there’s no point in it. If I aim at something Snit-like but not precisely identical, can I do significantly better? Are there holes in 257 where a little more functionality would be a really big help? Are there aspects of 257 that cause me serious problems?

To answer these questions, I’m embarking on a project called iGloo, a name I came up with a couple of years ago when Snit was gaining in popularity and I had begun to wish I’d called it something else. If I decide that it’s worth doing a perfect clone of Snit on top of 257, iGloo might just be a research project. If I decide that perfect compatibility with Snit is too constraining, iGloo might go on to be Snit’s replacement. Either way, it should be fun.