Scope/context of anonymous functions/procedures
19:50 26 Sep 2023

I'm trying to make a function that is a do-while loop (to iterate a linked list by key sequence) that can accept a variable that is the loop body. My question has to do with passing the body script as a variable to another function/procedure.

In Javascript, I just pass an anonymous function. In Tcl, the scoping is different; I don't know what it is called but it is not lexical scoping. If I try an anonymous procedure, the variables that are in the anonymous procedure but not in the one to which it is passed, are treated as new variables in that procedure's scope. Therefore, I tried the uplevel upvar approach shown in Prof. Ousterhout's text and it works. And, I think I even understand it. But it appears the reverse of the way I've thought of it in JavaScript.

This tiny example, illustrates what I mean. In JS, I've always thought that I was passing the anonymous function to the other function (callee?) and it was executed in its scope. But, in Tcl, that thinking doesn't seem to work; for it appears that a variable name is being passed to the callee procedure to provide a loop variable in the callee's scope back to the context of the caller procedure, and the anonymous procedure is execute in the context of the caller.

My question is, in both Tcl and JS, in these example, is the anonyProc being executed in the context of LoopBody or is it really executed in the context of Complex and the loop counter from LoopBody is made available to it? Is there really a difference in what takes place in the code that processes the script or is it just two ways of writing the same thing?

I've one additional question specfic to Tcl, which is, Can this be done with an anonymous procedure and using apply? My first attempt is at the very end and the variable value is in the context of LoopBody rather than Complex.

proc LoopBody {upvarName startKey endKey anonyProc } {
  upvar #1 $upvarName i
  for { set i $startKey } { $i < $endKey } { incr i 1 } {
    uplevel $anonyProc
  } 
}
proc Complex {} {
  foreach value {100 200 300} {
    LoopBody key 10 15\
        { 
          puts "key $key value [incr value $key]" 
        }
    puts ""
  } 
}
Complex

key 10 value 110
key 11 value 121
key 12 value 133
key 13 value 146
key 14 value 160

key 10 value 210
key 11 value 221
key 12 value 233
key 13 value 246
key 14 value 260

key 10 value 310
key 11 value 321
key 12 value 333
key 13 value 346
key 14 value 360

function loopBody (startKey, endKey, anonyProc) {
  for( let i = startKey;
        i < endKey;
        i++ ) {      
     anonyProc(i);
  }
}

function complex() {
  [100,200,300].forEach( (value,key) => {
     loopBody(
        10,
        15,
        (x) => {
          console.log(`x: ${x} value: ${value += x}`);
        }
     );
     console.log('');
  });     
}

complex();

Failed anonymous procedure attempt.

proc LoopBody {startKey endKey anonyProc } {
  for { set i  $startKey } { $i < $endKey } { incr i 1 } {
    apply $anonyProc $i
  }
}

proc Complex {} {
  foreach value {100 200 300} {
    LoopBody 10 15\
        { { x } {
            puts "x $x value [incr value $x]"
          }
        }
    puts ""
  }
}

Complex

x 10 value 10
x 11 value 11
x 12 value 12
x 13 value 13
x 14 value 14

x 10 value 10
x 11 value 11
x 12 value 12
x 13 value 13
x 14 value 14

x 10 value 10
x 11 value 11
x 12 value 12
x 13 value 13
x 14 value 14
scope tcl