News for hackers. Stuff that matters.
Re:The Birth of Tcl by jrapdx3 on Thursday April 21, 2022 @06:01PM (#31115719)
Having used Tcl extensively I think scoping rules aren't really too complicated, but there are some apparent inconsistencies. A main one is global variables aren't visible inside procedures unless declared as "global", whereas procedures are global no matter where created (in the global namespace). However referring to variables and procedures using fully qualified namespaces is always correct and unambiguous.

The thing with upvar (and uplevel) is that by default upvar refers to variables in the caller of the current proc. But when a proc is called indirectly (e.g., a callback proc) then the variable upvar is intended to reference may be 2 or more frames above. Using something like "upvar #2 refvar var" works as expected. It's analogous to deferencing pointers in C when there are >1 levels of indirection.

Is there any programming language that doesn't have some confusing rules? If a language is going to be useful the answer is bound to be "no". It's a curious how we get used to a particular (or even peculiar) syntax. When used long enough it begins to feel "natural" and obvious, and find it amazing how anyone would have difficulty understanding how it works.

Re:The Birth of Tcl by bitwize on Thursday April 21, 2022 @11:21PM (#31117833)
Ah, so Tcl's rules are dynamic scoping with extra steps. No wonder they're so mind-boggling.

Lexical scoping is the most straightforward, least surprising scoping strategy I've seen. With lexical closures it allows for some tremendously powerful constructs.

Re:The Birth of Tcl by jrapdx3 on Friday April 22, 2022 @02:33AM (#31118871)
True, lexical scoping is dominant in current programming languages and it is easier to reason about.

Alas Tcl is more complicated. It's sort of dynamic but not quite depending on how variables are accessed. For one thing a proc definition provides a separate scope from the global variable scope. If we define:

    set b 5 ;# global
    proc w {a} {
      # upvar 1 b b
      + $a $b ;# "can't read 'b': no such variable"
    }
    proc x {} {
        set b 2 ;# local
        w 0
    }
    x ;# error re: no such variable 'b'
If it was truly dynamic (or more correctly indefinite) scope, we'd see '2' printed because 'b' would be readable in procedure w, but instead we get an error. However if inside the procs '::b' was used instead of 'b', then 2 is printed consistent with dynamic scope. ('::b' is 'b' in the global namespace.)

Of course the call to w in x doesn't operate under lexical scope since procedures are global and can't access variables inside another procedure's scope.

It is possible under some conditions to mimic lexical scope using the upvar command. In the case of proc w, uncommenting the upvar call would reference var b in the calling proc (or stack frame).

Wow, written out that is kinda "mind-boggling"! But in practice it's not a problem keeping it straight, at least most of the time...