Updated 2016-09-18 04:22:49 by pooryorick

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.