TOOT with {*}

WHD: This is a slightly different take on Neil Madden's TOOT concept. The basic notion of TOOT is that a complex data value has its type embedded in it in such a way that if the value is used as a command, a special version of unknown can figure things out and call the correct method. In my view, using unknown for this purpose is a hack--useful for prototyping, but slow and fragile. However, Tcl 8.5's new {*} syntax offers an alternative.

For example, suppose you define a matrix data structure, implemented as a list of lists. Make it look like this:

 {matrix {{1 2 3}
           {4 5 6}
           {7 8 9}}

Now, define "matrix" as a Tcl command, like so:

 proc matrix {matrix method args} {
     # Execute $method on $matrix with $args
 }

Suppose "matrix" defines a method "nrows" that returns the number of rows in the matrix. You could then call the method like this:

 set m [{*}$matrix nrows]

I grant you, that {*} in the middle of the expression is ugly. However, it's been suggested that in Tcl 9 we could add the backtick as a synonym for {*}. That would give us this, which is much nicer.

 set m [`$matrix nrows]

This is not a perfect solution; ideally, we'd have a mechanism that would automatically translate

 $matrix nrows

to the command

 matrix nrows $matrix {*}$args

so that the "matrix" command could be implemented using namespace ensemble, and no further syntax would be needed.

Lars H, 2015-07-30: As of Tcl 8.6, this should be done as an ensemble with parameters (the -parameters option of namespace ensemble). It lets you have arguments before the method name, so that [{*}$matrix nrows] with the exact $matrix value you gave Just Works.

jcw - Interesting. How about: a variable expansion at the start of a command is always {*}-ed? Or is that too much magic...

RS: This has been discussed in the chat more than once, under the term Auto-expansion of leading word. I am all for it, as it offers many more possibilities, including lambda.

DKF: It's really Paul Duffin's ideas that we're building on here. [1 ] They've been transmogrified over time somewhat, but that's where I've been coming from for a while.

NEM: Funnily enough, I was about to mention TOOT to you (WHD), after reading your fascinating descriptions of Ramble and matrices. Yes, using unknown is slooow. Tcl 9 design decisions will float or sink TOOT. (For some early random thoughts on this, see: [2 ]). I've toyed with creating TOOT type commands with a trailing : at times, so it would be {matrix: {...}}. This allows you to have a "matrix" ensemble which defines all the methods, and a "matrix:" type-command which does the necessary argument juggling. But that's just syntax. Donal's correct about the influence of Paul Duffin in all this. About time for me to re-read that Feather paper...

WHD: And indeed, it was Ramble's matrix implementation that got me thinking about this--how could one give a data structure like that reasonably fast subcommands. Snit is good for many things, but not for that, not if you're interested in efficiency, at least not as it's currently implemented. (Ensembles might speed things up a bit.) And then, the model of objects-as-commands is very Tclish and useful, but also very heavy weight. There's no reason for a stack to be represented as a command, for example. A list is perfectly good; you just want a bit of sugar. The form {stack {value list....}} makes excellent sense in this case. And {*} does 90% of the work.

RS 2005-12-29: I usually use interp alias for lighterweight objects-as-commands:

 interp alias {} $obj {} ${class}::dispatch $obj

But TOOT does the same job in its own way...

NEM I've been coming around to this way of thinking recently too. I've been using a "define" command (similar to the one used in fold) to make working with TOOT objects easier and avoid an unknown lookup. The code then looks something like:

 proc define {name = args} { interp alias {} $name {} {*}$args }
 define foo = Person: "Neil Madden" 25
 puts "[foo name] is [foo age] years old"
 puts "foo is [foo]" ;# prints "foo is Person: {Neil Madden} 25"

(I've developed a habit of using Type: as a combined constructor/dispatcher function).

WHD Coming back to this a decade later…the "interp alias" mechanism is light weight, which is neat…but you still have a command to clean up when you're done.