PYK 2016-09-15: In the
Tcl Chatroom recently,
dbohdan mentioned that in
Why Ruby is an acceptable Lisp (2005) (randomhacks.net)
,
pjmlp
mentioned working for a startup that had a
Rails-like stack, but implemented in a mix of Tcl, Apache and
C. This lead me to read
Why Ruby is an acceptable LISP
, Eric Kidd, 2005, and then to the venerable
Revenge of the Nerds
, Paul Graham, 2002. Where other languages provide
closures, Tcl provides
namespaces, which favor "explicit" over "implicit": Each namespace has a name, and its lifespan is under the manual control of the programmer. Below is a procedure,
stowroutine, that simply creates a routine that has its own namespace to work in. A helper procedures,
stowproc, exposes a stowroutine as a command, and another helper routine,
stowsow, plants an existing routine in a new namespace. This suite of procedures accomplishes much the same thing as AMG's
sproc, but in a more minimal way. Since an entire namespace is at its disposal, a
stowroutine is a flexible creature. One thing to remain aware of is that management of the lifetime of the routine and of its namespace is delegated to the programmer.
A
coroutine is of course another natural choice for this sort of thing, and it may even be useful to combine the two mechanisms to create a
stowcoro.
#! /bin/env tclsh
proc stowroutine {ns argspec body} {
list ::apply [list $argspec $body [
uplevel 1 [list namespace eval $ns {namespace current}]]]
}
proc stowproc {name routine} {
uplevel 1 [list interp alias {} [namespace current]::$name {} {*}$routine]
}
proc stowsow {routine namespace args} {
set namespace [uplevel 1 [
list namespace eval $namespace {namespace current}]]
set spec [lindex $routine 1]
set spec [lreplace $spec[set spec {}] 2 2 $namespace]
lreplace $routine[set routine {}] 1 1 $spec
}
Example:
set p1 [stowroutine p1 count {
variable a
incr a $count
}]
stowproc p1 $p1
stowproc p2 [stowsow $p1 p2]
puts [{*}$p1 7] ;# -> 7
puts [p1 8] ;# -> 15
puts [p2 3] ;# -> 3
At first,
stowproc looked like this:
proc stowproc {name routine} {
uplevel 1 [list proc $name args "::tailcall {*}$routine {*}\$args"]
}
However, this folded
$routine into a new string, so procedures based on the same stowroutine didn't share the byte-compiled compiled routine. The
interp alias variant of
stowproc fixes that.