[PYK] 2016-09-15: In the [Tcl Chatroom] recently, [dbohdan] mentioned that in [https://news.ycombinator.com/item?id=12470715%|%Why Ruby is an acceptable Lisp (2005) (randomhacks.net)], [https://news.ycombinator.com/user?id=pjmlp%|%pjmlp] mentioned working for a startup that had a [Ruby%|%Rails]-like stack, but implemented in a mix of Tcl, Apache and [C]. This lead me to read [http://www.randomhacks.net/2005/12/03/why-ruby-is-an-acceptable-lisp/%|%Why Ruby is an acceptable LISP], Eric Kidd, 2005, and then to the venerable [http://www.paulgraham.com/icad.html%|%Revenge of the Nerds], Paul Graham, 2002. Where other languages provide [closures], Tcl provides [namespace%|%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` like this: ====== proc stowproc {name routine} { uplevel 1 [list proc $name args "::tailcall {*}$routine {*}\$args"] } ====== However, this folds `$routine` into a new string, meaning that procedures based on the same stowroutine didn't share the byte-compiled compiled routine. The `[interp alias]` variant of `stowproc` fixes that. <> closures