Version 3 of Procedures stored in arrays

Updated 2002-11-21 17:40:30

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.

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 sprocs 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:

  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 and Tequila for something interesting.