Hacker News new | past | comments | ask | show | jobs | submit login

1) I realize that Tcl isn't technically stringly typed on the inside anymore, but extensionally it still appears so.

2) Attempting to understand how variable scoping works in Tcl is like attempting to stare into the maw of some eldritch horror, deeper than the earth itself by some trick of other-space, and lined with infinite rows of writhing, fang-tipped cilia. The central tenet of lexical scoping -- that bindings visible outside a set of braces should also be visible inside unless overridden with a specified inner binding, didn't occur to Ousterhout when he designed Tcl and Tcl has to deal with the implications of that. 'upvar' sometimes works, but sometimes doesn't, and in some cases you have to reason through when your code is being called and what's visible then.




I just guess there are two kinds of people in the world. I think scope leakage across logical boundaries is dangerous and really hurts maintainability and code readability, and I think Tcl's tight restriction of variable scope by default is one of its most valuable properties.

I'm so old I remember many conversations among perl and JavaScript programmers talking about the bug that drove them craziest, and it always seemed to come down to unexpected scope leakage and variable name collisions. Then there would be a lot of laughing and back-slapping and calls of 'been there bro!' I just can't wrap my head around that viewpoint. Better IMO to allow scope to cross function point boundaries only at well-defined points.

I've been programming in Tcl for over 20 years and the times when use of upvar was called for have been very few. Meanwhile, how much of your code that you wrote 20 years ago still runs just fine on the latest tool stack?


I don't think that downward capture of bindings from an enclosing scope counts as "leakage" and I've never seen it lead to the maintainability issues you describe. In Perl and JavaScript, under certain conditions variable scope can leak upward, and that opens up its own can of fresh hell.

Lexical scope also allows the lambda calculus to work, and with it powerful constructs like parameterized functions over values, and fine-grained restriction of variable access through lexical closures. An eager young Scheme programmer can write a module or object system for Scheme in an afternoon.

Tcl allows those things to be written, but they're a bit more involved. And you have to exercise caution, as which bindings are live when a particular Tcl script gets executed is not clear from the program text. (I believe Tcl with upvar actually has dynamic scope semantics which, yet again, can of hell.) But hey, it's a wonderful thing that Tcl admits as much metaprogramming as it does, that puts it well ahead of many popular languages.


I was thinking exactly in the eldritch horror direction. Thanks for writing that, you have saved me the unpleasantness of finding out exactly what it was that made me loathe Tcl.


What's not to love about `upvar`? /s


Upvar has it's place, especially when writing your own control structures. Outside of that, tread lightly.


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.


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.


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...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: