Snit's Not Incr Tcl

Description

Snit's Not Incr Tcl (Snit for short) is a module of tcllib.

It is pure-Tcl object framework which relies on object composition and delegation rather than object inheritance. It can be used for both GUI objects (i.e., Tk megawidgets, aka Snidgets) and for non-GUI objects.

Snit used to be a software package on its own, hosted separately on the Snit Home Page [L1 ]. For some reason the old home page is not removed thus only offer historical information. See the Tcllib documentation for the latest information on Snit.

As of 2010, Tcllib ships both Tcl-8.5-depending Snit v2.3.1 and Snit 1.x that works with Tcl 8.3 and 8.4.

Will's Snit page is out-of date, missing references to the tcllib distribution of snit and the tcllib code repository where Snit development takes place.

WHD is currently working on a Snit-like layer for TclOO called Igloo.

Usage

package require snit

(note the lower case s) to include the package.

Documentation

man page
FAQ

Community

There is a Snit mailing list; discussion topics will include Snit usage, but also (and more importantly) future development. If you're a Snit user or are interested in how it evolves, you can subscribe here

There is minimal traffic on the mailing list.

Testimonials

NEM: "Snit really is excellent. I use a very similar idea in my tkbrowser code, but a bit less polished. I may possibly update my code to use snit now."

stevel: "it is non-intrusive, and the delegation model fits nicely with Tk ... Will Duquette has done a nice job."

Jeremy Miller: "So easy, even I can use it."

KMG: "An outstanding contribution to Tcl/Tk, great concepts, high class implementation and excellent doco."

RLH: "I have been playing around with Snit for a couple days now and I think it is really cool."

wdb: "At first glance, Snit is much more 'Tcl'ish than Itcl. Moreover, I can (and will) switch to Snit without the needs to waste my earlier Itcl-thingies. -- Some improvements are possible, nonetheless."

Examples

WHD's Athena project is a goldmine of snit example code, with many useful widgets, types and other goodness.

Widgets and other functionality

Applications

Articles

Wikipedia
My favorite Tcl package
by Andreas Kupries ,2010-04-26

See Also

dict in snit
snitvfs
iGloo
a project to see what's involved in building Snit on top of TIP #257: Object Orientation for Tcl.
A simple comparison of Tcl object extensions
Tcl OO Bench
Notebook App - User Supplied Patches
Snit under Tcl/TK 8.3
Optimizing Snit
Itins
attempt to emulate Snit with Incr Tcl
Xoins
attempt to emulate Snit with XOTcl
Snit like delegation in XOTcl
snitwiz
Category Snit Widgets
snitdom
Experiment with data manipulation and Snit
AM 2008-10-17: After a suggestion by Michael Baudin I looked into Snit to create an object-oriented interface to numerical functions

Also, see below for a read-only text widget written using Snit.


Details

While developing Notebook App I decided I was tired of writing object-like commands by hand, and wanted a little help. And none of the Tcl object systems that I'd seen really gave me what I wanted, which was to create new objects by delegation rather than inheritance. If you rely on inheritance, then you're tied into that object system. But in Tcl, any command that acts like an object should be treated as an object. Hence, Snit, which doesn't provide for inheritance, but which makes it really easy to build an object out of other objects by delegation, regardless of where those objects originated.

(I should add, I don't have anything particular against Incr Tcl; but the name Snit occurred to me, and then wouldn't go away.)

Just as an example, here's a Read-only text widget written using Snit V0.95:

package require Tk
package require snit 0.95
::snit::widgetadaptor rotext {

    constructor {args} {
        # Turn off the insert cursor
        #installhull using text $self -insertwidth 0 
        # DDG the $self gaves an error at least with 0.97 onwards
        installhull using text -insertwidth 0

        # Apply an options passed at creation time.
        $self configurelist $args
    }

    # Disable the insert and delete methods, to make this readonly.
    method insert {args} {}
    method delete {args} {}

    # Enable ins and del as synonyms, so the program can insert and
    # delete.
    delegate method ins to hull as insert
    delegate method del to hull as delete
    
    # Pass all other methods and options to the real text widget, so
    # that the remaining behavior is as expected.
    delegate method * to hull
    delegate option * to hull
}

WHD: This isn't pseudocode; this is actual code that was used in version 1.x of Notebook App.

VK 2006-10-03: Thanks for the code, now I'm using it in Tcl::Tk. I don't understand why the need for hiding a cursor? Line "# Turn off the insert cursor" and few lines below it just make things more complicated to the user. IMO having the cursor in read-only widget for navigation and selection is good.

So please shrink the code even more and thus make it better! :) :)

BTW: "rotext" is a really useful widget; I'm curious why it's not in the core widget set...

Stack Example

Chang LI: The example of stack:

::stack
% stack create mystack
::mystack
% mystack push a
% mystack push b
% mystack peek
b 
% mystack pop
b
% mystack peek
a

I like the $mystack representation and you can easily think it as an object. This is my prefer representation:

stack mystack
$mystack push a
$mystack push b
set x [$mystack peek]
puts $x
$mystack pop
$mystack peek

WHD: I gather what you're wanting here is for the new "stack" command to treat "mystack" as a variable, generate an object command with a unique name, and pop it in mystack. I really don't want to do that; it's not like Tk, to begin with, and that's essential. But second, when you're using widgettype to create a megawidget, there's no reason with the hull widget needs to be created in the widgettype's constructor. There are times when you want to create the hull, and have a widgettype that creates and manages its contents. The BWidgets "Notebook" widget is an example. It wants to create the individual frames in the notebook, and hand them to you. Snit is perfectly happy to accept such a frame, and then take it over.

RS: But Chang Li can easily have his style (as also often done in Tk), by:

set mystack [stack mystack]

given that the stack command returns the name (as Tk widgets do).

WHD: If I understand what he's asking for, that's not quite the same, as he has to provide the object name himself. In his example, it looks like the "stack" command is providing the object name. If you're creating hundreds of objects of the same kind, I can see that automatic name generation might be helpful.


WHD: Update, 11 October 2002: You can now use "%AUTO%" in object names to get an automatically generated name, e.g.,

set mystack [stack %AUTO%]
$mystack push a
set a [$mystack pop]

escargo: Is there any reason for the user to retrieve the generated name? If so, is there a standard way of doing so? If you have the generated name, is there any way to tell what kind of object was created? Is there an advantage in doing so?

WHD: Yes, there is; the name is the object command, so obviously you have to know it to use the object. Where you stash it is up to you, and consequently how you retrieve is up to you. Or am I missing something? As for looking at the name--%AUTO% is replaced by something that looks like "<typename><counter>", where <counter> starts at 1 for the type and increments each time %AUTO% is used. In the example above, the name would probably be "stack1". Two other wrinkles: first, you can specify a longer name with %AUTO% embedded in it (e.g., .foo.bar.%AUTO%); and second, Snit makes sure that the name isn't already in use. If it is, it keeps incrementing the counter until it has something unique. Is there an advantage to having the type name in the generated name? It might help with debugging, I suppose.

escargo: If I understand correctly the result returned by [stack %AUTO%] is the name of the object. So

set mystack [stack %AUTO%]
puts $mystack

would output something like "stack1". That's right -- WHD

I don't necessarilly think having the type name in the generated name is a requirement, but there should be a function that given a generated name can return the type name. (For symmetry, it might be nice to be able to have another function that given a generated name can return the instance number.) Having the generated name be something easily split might be useful, just in case somebody defines a type "stack1" and a "stack2". (Or is there a rule that says that typenames must be purely alphabetic?)

Certainly the object itself should also be able to provide its type name if politely asked.

It can: [$mystack info type] returns the type name. -- WHD


lv: any thought of a snit command like info that will return names of all snit objects of a specific class?


Also, it would seem like natural extension to have a zero-arg constructor (e.g., [stack]) that implies %AUTO% as the default first argument.

WHD: That's a good idea. Snit V0.7. :-)

escargo: Or maybe Snit V0.8....

WHD: Actually, I've decided not to do this, as it doesn't work for megawidgets, and it was yet another gratuitous difference between snit::types and snit::widgets.


RS: proposes to use "#auto" (number automatic) as in Incr Tcl - I know Snit is not, but reusing knowledge can make things easier...

WHD: I thought about doing that originally, and rejected it for two (not particularly important) reasons. The first is, I don't like using "#" for anything but comments; the Emacs editing mode I use always wants to put a "\" in front of every non-comment "#" whether it needs it or not. The second is, I've started using "%placeholder%" as a standard for replaceable parameters in strings when normal variable interpolation is inappropriate. I can reconsider the decision if enough people think it's a good idea.

WHD After a poll of folks on the Snit mailing list, I've decided to leave it as it is.

DKF: FWIW, I think that objects ought to always be created with an automatic name under normal circumstances, and that it is up to the caller to rename them if they want them to have a specific name.


What about a 'Snit lite', that just implements snit::type, maybe as a Tcl-command 'widget' (or 'object' or 'gadget' ;-)? If you're just programming in Tcl (and not use Tk at all or use another toolkit or are happy with Tk as it is) it would be nice to have such a mechanism to create and use abstract 'widgets' (objects) in a total natural, intuitive Tcl'ish way. I like Snit, by the way.

Actually I think that a 'widget' command is desperately missing in Tcl and should be included in the core. That would be the 10% of OO that could be enough to get 90% of OO's handyness without any of the trouble. Does someone get what I mean? That would be the step beyond 'proc' that more often than not could make the difference between nastyness and fun in everyday scripts.

WHD: Actually, Snit works perfectly well in non-Tk code--if Tk isn't available, then snit::widget and snit::widgetadaptor aren't either, but snit::type works perfectly well.

joh: It works without any problem, but if you're not using Tk at all, dragging all that with you and constantly reading about Tk in the docs just feels wrong. (The point is that I'm in the very process of tossing Tk for good finally. I almost exclusively use gnocl now and run constantly into extensions that work perfectly well without Tk but treat Tk as if it were a part of Tcl. Which it isn't). It also makes the docs look much more cluttered than it needs to. Maybe I'm just pedantic, but this somehow hurts that quite clean feeling that Snit gives me otherwise.

WHD: There's actually not that much code that's Tk-specific; you're not dragging that much around with you. From my point of view, having a single solution that supports both GUI and non-GUI objects is a good thing, not a bad thing.


CMcC: I want to use a snit object in a callback. Not a snit method, but an actual instance, as follows: Given instance O, I want an O' such that [eval O' $method {*}$args] will invoke $method on instance O with $args.

I note that [mymethod] called without args will give me a plausible-looking formulation, am I ok to just use that as O'?

WHD: Yes, exactly.


CMcC: I need the type signature of a snit method, in a similar form to standard tcl's [info args]. Is there any sanctioned way to do this?

Meanwhile, I've run this proc up to do it, could it be made into a [$obj info signature] facility within Snit?

proc methodsig {obj method} {
    set xcmd "[$obj info type]::Snit_method$method"
    if {[info procs $xcmd] ne {}} {
    set a {}
    foreach var [lrange [info args $xcmd] 4 end] {
        if {[info default $xcmd $var value]} {
            lappend a [list $var $value]
        } else {
            lappend a $var
        }
    }
    return "$method $a"
    } else {  
        error "No such method $method in $obj of type [$obj info type]"
    }
}

WHD: The above proc will only work for non-delegated methods. Making it work for delegated methods is difficult--and impossible, in some cases.


LV: I was wondering whether anyone has investigated making use of Tile in Snit - at least when available.

WHD: I've not tried it, but given that Tile widgets are widgets, and can be packed or gridded in standard frames and toplevels, I don't see any reason why it shouldn't work just fine. That said, I wouldn't be surprised if there were some quirks. But I'm not expecting any.


AMG: How about a [namespace ensemble] [L2 ] -enabled version of snit? Actually I find it really curious that same isn't already available.

(Update, 2005-Mar-20: Where the freg [L3 ] did the "ghttp" come from? I wanted an [N]-style link. Wikibug?

RS: Seems so: the URL parser got confused by the dash behind the URL. Inserting a space helped. Lars H: The basic problem is that Wikit first looks for free-text links and only later starts considering brackets. See my note on Wikit Problems for more details.)

CMcC: I think namespace ensemble was created in consultation with WHD, so we can hope there'll be an ensemble-enabled snit.

WHD: Trust me, getting ensembles into Snit is something I'd dearly love to do--it should speed everything up quite amazingly. It doesn't exist yet because I've got limited time, multiple projects, and Tcl/Tk 8.5 isn't stable yet. I don't have access to a ready-to-install Tcl/Tk 8.5 for OS X, and I haven't made the time to download and build my own. Trust me, though, the day is coming. I don't know the day or the hour (nor yet the month) but the day is coming.

LV: Why not just download the Tclkit for MacOS X PPC and Tcl/Tk 8.5 - should have what you need. See [L4 ] for the details. Might not be the latest and greatest - but I'd guess it's relatively recent.

WHD: Actually, I just now discovered that Tcl/Tk Aqua 8.4.9 includes executables for 8.5 as well.


AMG: itunesdb uses Snit but I'm considering (temporarily/experimentally) switching it to namespace ensembles. Would having such an example be a help? And why don't we have a namespace ensemble page yet? Somebody go write one!!


RLH: Is there a comprehensive tutorial for Snit somewhere?

WHD: At present, the Snit FAQ is the closest thing there is to an on-line tutorial. There's a possibility that I might be doing a half-day Snit class at the Tcl conference in Portland this fall, though.

RLH: While I could not attend (unless that is Portland, Maine) I would eagerly devour any info you put up about it. I will look through the FAQ and as much code I can find. Thanks for Snit!

Despite Will's diffidence, such witnesses as Aric Bills testify that, "The Snit FAQ is an excellent tutorial ..."


escargo 2005-05-25: Luban is a new scripting language with composition as one of its central features: "Luban is a component oriented scripting language."