Tcl doesn't have nested procedures. You can call proc
inside a procedure definition, but that's just creating a normal procedure (the namespace used for resolution of the name of the procedure to create will be the current namespace of the caller, as reported by namespace current
).
Why would you put proc
inside proc
? Well, the real reason for doing so is when you want to have the outer command act as a factory, to create the command when it is called. Sometimes the name of the command to create will be supplied by the caller, and sometimes it will be internally generated (in the latter case, it is normal to return the name of the created command). The other case that comes up is where the outer command is some sort of proxy for the (real) inner one, allowing the postponing of the creation of the real command because it is expensive in some fashion; if that's the case, the inner procedure will tend to actually be created with the same name as the outer one, and it will replace it (though not the executing stack frame; Tcl's careful about that because that would be crazy otherwise).
In the case where you really need an “inner procedure” it's actually better to use a lambda term that you can apply
instead. That's because it is a genuine value that can be stored in a local variable and which will automatically go away when the outer procedure terminates (or if you explicitly unset
the variable or replace its contents). This inner code won't have access to the outer code's variables except via upvar
; if you want to return the value while still binding variables, you should use a command prefix and include a bit of extra trickery to bind the variables as pre-supplied arguments:
proc multipliers {from to} {
set result {}
for {set i $from} {$i <= $to} {incr i} {
lappend result [list apply {{i n} {
return [expr {$i * $n}]
}} $i]
}
return $result
}
set mults [multipliers 1 5]
foreach m $mults {
puts [{*}$m 2.5]
}
# Prints (one per line): 2.5 5.0 7.5 10.0 12.5
Using an inner proc
to simulate apply
Note that the apply
command can actually be simulated by an inner procedure. This was a technique used in Tcl 8.4 and before:
# Omitting error handling...
proc apply {lambdaTerm args} {
foreach {arguments body namespace} $lambdaTerm break
set cmd ${namespace}::___applyLambad
proc $cmd $arguments $body
set result [uplevel 1 [linsert 0 $args $cmd]]; # 8.4 syntax for safe expansion!
rename $cmd ""
return $result
}
This was somewhat error-prone and very slow as it would recompile on each invocation; we don't do that any more!