William Wragg 21-nov-2002 - While playing with [Lua] I discovered the joys of first class functions. This combined with the use of associative arrays was so much fun to program with that I started wanting them in other languages I use. I found a similar idea in [Arrays as cached functions] but this wasn't quite what I wanted. What I wanted were for procs to be stored in arrays, and then called by just getting the value of the array. (Also see [Arrays of function pointers] - [KPV]) This is what I came up with: # unique integer ID generator, at first call gives 1, then 2, 3, ... proc intgen {{seed 0}} { set self [lindex [info level 0] 0] proc $self "{seed [incr seed]}" [info body $self] set seed } # Lambda proc with caller scope life dependenecy. # When the caller scope dies so does the lambda proc. proc sproc {args body} { set name sproc[intgen] uplevel 1 set __$name - uplevel 1 "trace variable __$name u {rename $name {} ;#}" proc $name $args $body return $name } # Use this proc to run the sprocs stored in arrays. # The value from the array - sproc - is eval'ed to produce # the proc and name. This proc name and the args list is # then eval'ed again to produce the output. proc call {sproc args} { eval [eval $sproc] $args } The '''sproc''' procedure is just a lambda procedure from [Lambda in Tcl]. To use the above do something like - note the use of the global namespace for accessing the array value: set s(spfac) { sproc x { expr {$x<2? 1: $x * [call $::s(spfac) [incr x -1]]} } } Which is the same as: proc pfac x { expr {$x<2? 1: $x * [pfac [incr x -1]]} } You use the '''sproc''' like: call $s(spfac) 30 1409286144 Another simple example would be: set arr(hello) { sproc var { puts $var } } call $arr(hello) "hello world" hello world The overall affect is almost what I wanted. The way of calling the '''sproc'''s is a bit gludgy, but all other ways I could think of used traces, which I didn't want. I wanted everything to be in the array and a few handler procs. Creating and destroying procs is not the fastest process in the world but there are other ways - see below: puts pfac:[time {pfac 30} 1] pfac:81 microseconds per iteration puts spfac:[time {call $s(spfac) 30} 1] spfac:2749 microseconds per iteration But hell it was fun figuring it all out. These could even be combined with [Persistent arrays] or [Tequila] for something interesting. ---- [KPV] My biggest problem with ideas like this and also things like [Arrays of function pointers] and even object inheritance is that it can make it very hard for someone new to a code base to read code and understand what's going on. Trying to determine which actual procedure gets call at a given line of code can be almost impossible to determine without actually stepping the code in a debugger. Also, how do you debug a procedure stored in an array? [RS]: The procs themselves are not in the array - that contains only the generated names. Having that, you can introspect e.g. with info body $s(spfac) ---- [RS]: If I read your code right, ''call'' creates a new lambda every time, with constant body. I think it's much easier to do it like this: set s(spfac) [sproc x {...}] ;# define only once $s(spfac) 30 ;# call by dereferencing the array element ..and for easier reading I would still use ''lambda'' for ''sproc'', 'cause that's what it's called in the literature too... ---- WW: The above is not really what I wanted. I wanted to store the procedure in an array, so that if the array was modified, the procedure when run again would use the updated code, and I wanted this without traces present, unless this could be combined some how so that it was all automatic. I also wanted to be able to use the standard array procs for copying, changing etc. the ''sprocs'' I called the lambda part of the helper procs '''sproc''' as I was going to add some additional stuff which moved '''sproc''' away from a lambda proc, infact away from a proc all together. The stuff below is what I was going to add. Your all too fast for me :o) proc call {sproc args} { if {[llength $sproc] != 3} { error "wrong # args: should be \"sproc args body\"" } elseif {[lindex $sproc 0] != "sproc"} { error "Not a stored procedure: should be \"{sproc args body}\"" } else { set arguments [lindex $sproc 1] set code [lindex $sproc 2] set len_arguments [llength $arguments] set len_args [llength $args] set llen_arguments [expr $len_arguments - 1] set llen_args [expr $len_args - 1] # Set the variables to the given args after checking validity. if {$len_args < $len_arguments} { error "wrong # args: should be \"$arguments\"" } elseif {($len_args > $len_arguments) && [lindex $arguments $len_arguments] != "args"} { error "wrong # args: should be \"$arguments\"" } else { for {set i 0} {$i < $llen_arguments} {incr i} { set [lindex $arguments $i] [lindex $args $i] } if {[lindex $arguments $llen_arguments] == "args"} { set args [lreplace $args 0 [expr $llen_arguments - 1]] } else { set [lindex $arguments $llen_arguments] [lindex $args $llen_arguments] } # Run the code. eval $code } } } The above proc replaces the three (sproc, intgen, and call) original helper procedures with just the one '''call''' proc. The ''sproc'' is kept as a flag, so that arrays which store procedures and data together can differentiate easily between them. The single '''call''' proc is also faster: puts spfac:[time {call $s(spfac) 30} 1] spfac:1358 microseconds per iteration The stored procedures can be traced like any other variable to show when one has been called or altered etc...: set s(hello) {sproc {var} {puts $var}} trace variable s(hello) r {puts "hello called" ;#} call $s(hello) "hello there" hello called hello there Built in procs can be wrapped in an ''sproc'' so that changes can be made to the ''sproc'' without affecting the builtin one: set builtIn(puts) { sproc {args} {eval puts $args}} Well I think that wraps it up. I might try a small fully disributed and persistent app with these at some point, just for fun. :o)