Version 5 of Let unknown know

Updated 2002-11-05 18:03:55

Richard Suchenwirth - To extend Tcl, i.e. to make it understand and do things that before raised an error, the easiest way is to write a proc. Any proc must however be called in compliance with Tcl's fundamental syntax: first word is the command name, then the arguments separated by whitespace. Deeper changes are possible with the unknown command, which is called if a command name is, well, unknown, and in the standard version tries to call executables, to auto-load scripts, or do other helpful things (see the file init.tcl). One could edit that file (not recommended), or rename unknown to something else and provide one's own unknown handler, that falls through to the original proc if unsuccessful, as shown in Radical language modification.

Here is a simpler way that allows to extend unknown "in place" and incrementally: We let unknown "know" what action it shall take under what conditions. The know command is called with a condition that should result in an integer when given to expr, and a body that will be executed if cond results in nonzero, returning the last result if not terminated with an explicit return. In both cond and body you may use the variable args that holds the problem command unknown was invoked with.

 proc know {cond body} {
    proc unknown {args} [string map [list @c@ $cond @b@ $body] {
        if {![catch {expr {@c@}} res] && $res} {
            return [eval {@b@}]
        }
    }][info body unknown]
 }

if 0 {Condition and body are wrapped together and prepended to the previous unknown body. This means that subsequent calls to know stack up, last condition being tried first, so if you have several conditions that fire on the same input, let them be "known" from generic to specific. Here's a little debugging helper, if "know" conditions don't fire: }

 proc know? {} {puts [string range [info body unknown] 0 511]}

if 0 {Now testing what new magic this handful of code allows us to do. This simple example invokes expr if the "command" is digestible for it:

 know {[expr $args] || 1} {expr $args}
 % 1+2 * 3
 7

The "||(or) 1" appended in the condition lets this fire even if the expression would result in zero... I started these experiments because I wanted to have an infix index vector constructor (cf. Playing Haskell), so [1..4] returns {1 2 3 4}:

 know {[regexp {^([0-9]+)\.\.([0-9]+)$} [lindex $args 0] -> from to]} {
    set res {}
    while {$from<=$to} {lappend res $from; incr from}
    set res
 }
 % puts [1..5]
 1 2 3 4 5

Here's a weird experiment with a "reverse Polish" command (see RPN in Tcl):

 % know {[lindex $args end]=="say"} {puts [lrange $args 0 end-1]}
 % hello world say
 hello world

And another variation on infix assignment:}

 know {[lindex $args 1] == "="} {
    set name  [lindex $args 0]
    set value [lrange $args 2 end]
    if [catch {uplevel 1 set $name [expr $value]} res] {
        uplevel 1 set $name [list $value]
    } else {set res}
 }
 puts [i = 17]
 puts [j = $i + 4]

But as in radical language modification, this works only for variable names that are unknown as commands...


This is pure and utter evil. I like it!


I have wondered how hard it would be to connect unknown to a database or even a socket that would serve as the source for unknown procedures. I was thinking about how a local client can be customized to match a remote server.

In the two cases I am considering, the code that defines the procedures for editing data in a data base could be in the data base itself. When the client runs and needs to access a table that is defined (in the data base) but not previously accessed it would go to a data base table of tcl code (accessed using the table name as the key) and somehow get the code into the interpreter.

I was looking at the pkgIndex file and was wondering about what would happen if the code

 [list source .....]

was replaced with something besides source that would load code. Then I wondered, what is the equivalent of source? That is, if I have a string containing tcl code that is equivalent to the contents of a file of tcl code, what is the proper way to load it?

In the second case, instead of having the source code in a data base, the source would reside on a remote system. I would want to dynamically load it through a socket. (This sounds similar to the interface between Erlang and Tk.) So somehow unknown would need to do these steps:

  1. Inquire through a socket whether the remote system had a definition for a proc
  2. If it did, then transfer it through the socket.
  3. If it was received successfully, load it.

Is this kind of behavior a solved problem somewhere?

escargo 5 Nov 2002

RS: To evaluate a string, use eval of course ;-) source $file does basically:

 set fp [open $file]
 set data [read $fp]
 close $fp
 eval $data

So if you get your code over a network, just eval it (but maybe in a safe interpreter...)


unknown - Arts and crafts of Tcl-Tk programming