TIP #284: NEW 'INVOKE' AND 'NAMESPACE INVOKE' COMMANDS ======================================================== Version: $Revision: 1.23 $ Author: Miguel Sofer State: Draft Type: Project Tcl-Version: 8.7 Vote: Pending Created: Sunday, 01 October 2006 URL: https://tip.tcl-lang.org284.html Post-History: ------------------------------------------------------------------------- ABSTRACT ========== This TIP exposes a Tcl script-level interface to the direct command invokation engine already present in the Tcl library for years. PROPOSED NEW COMMANDS ======================= This TIP proposes a new subcommand to *namespace*, *invoke*, with the following syntax: *namespace invoke* /namespace/ /cmd/ ?/arg/ ...? This invokes the command called *cmd* in the caller's scope, as resolved from namespace /namespace/, with the supplied arguments. If /namespace/ does not exist, the command returns an error. This TIP also proposes a new command: *invoke* /level/ /cmd/ ?/arg/ ...? This invokes the command /cmd/ in level /level/ with the supplied /arg/uments. The syntax of /level/ is the same as that required by *uplevel*. RATIONALE =========== There is currently no script-level equivalent to /Tcl_EvalObjv()/, though the functionality is provided by one of: eval [list cmd ?arg ...?] {*}[list cmd ?arg ...?] Note that the core tries to optimise the first case, but has to be careful to only avoid reparsing when it is guaranteed safe to do so. The notation is rather clumsy too. The proposed new commands try to improve this situation, with the added functionality of determining the namespace in which the command name is to be resolved (functionality which was very difficult to use previously using the script-level API). In this manner it is possible for the invocation to make good use of *namespace path* and *namespace unknown* features. The new command *invoke* could be implemented as: proc invoke {level args} { if {[llength $args] == 0} { return -code error SomeMessage } if {[string is integer $level] && ($level >= 0)} { incr level } uplevel $level $args }] REFERENCE IMPLEMENTATION AND DOCUMENTATION ============================================ [RFE 1577324] (which depends on [Patch 1577278]) provides an implementation of *namespace invoke*. [] [] DIFFERENCES TO OTHER COMMANDS =============================== 1. Both these commands perform command invocation, as opposed to the script evaluation done by *eval*, *uplevel*, *namespace eval* and *namespace inscope* 2. *namespace inscope* does a magic expansion of the first argument, *namespace invoke* takes the first argument as a command name. In other words, *namespace inscope* can be used with a command prefix. /Feedback on the semantics suggest that this is a worthy feature, very useful for packing up command prefixes. This tip may yet be revised or withdrawn to take that into consideration./ 3. Both *namespace eval* and *namespace inscope* add a call frame,*namespace invoke* does not - it invokes in the caller's frame. SAMPLE USAGE ============== In tcllib's *::math::calculus::romberg* we see (edited for brevity): # Replace f with a context-independent version set fqname [uplevel 1 [list namespace which [lindex $f 0]]] set f [lreplace $f 0 0 $fqname] ... set cmd $f lappend cmd [expr {0.5 * ($a + $b)}] set v [eval $cmd] where the command name in the prefix /f/ is replaced with its fully qualified name. A further variable is lappended, and the result is sent to *eval*. With *namespace invoke* and *invoke* this would be coded as: set ns [invoke 1 namespace current] set f [list namespace invoke $ns {*}$f] ... set v [{*}$f [expr {0.5 * ($a + $b)}]] If both new commands took the /cmd/ argument as a prefix to be expanded (as suggested by some early commenters), it would be even nicer for this usage: set ns [invoke 1 [list namespace current]] set f [list namespace invoke $ns $f] ... set v [invoke 0 $f [expr {0.5 * ($a + $b)}]] The same thing (with slightly different semantics and a performance cost due to script evaluation instead of command invocation) could be obtained today by doing: set f [uplevel 1 [list namespace code [list uplevel 1 $f]]] ... set v [eval [lappend f [expr {0.5 * ($a + $b)}]]] In order to fully reproduce the semantics and performance it would have to be: set f [uplevel 1 [list namespace code [list uplevel 1 $f]]] ... lset f end end [linsert [lindex $f end end] end [expr {0.5 * ($a + $b)}]] set v [eval $f] COPYRIGHT =========== This document has been placed in the public domain. ------------------------------------------------------------------------- TIP AutoGenerator - written by Donal K. Fellows