Version 10 of Variable versus a Global Array

Updated 2004-07-08 19:16:24 by RHS

Keith Vetter 2003-09-14 : Everyone knows that global variables are bad, and that putting your variables into their own namespaces via the variable command is better. Variable is a more modern, oo'ish interface that scales better and leads to better encapsulation, etc.

Sorry, I don't agree.

Granted, unrestrained use of global variables can be bad, but I claim that with the proper use of global you can get most of the benefits that variable seems to provide plus a few more benefits as well.

My opinion comes from my experience in writing and maintaining a 10,000 line tcl program called Klimb (sorry, no link, the web site is down). The first half of the program I wrote just using global variables. Then I refactored much of the code, adding namespaces and namespace variables. All new development over the past year has been using namespaces and variables, but that may change due to my experience I'll relate here.

First off, the main benefits of variable are encapsulation and not polluting the global namespace, which also helps in avoiding name collisions when the code is used by others.

But I can get exactly these benefits from global variables if I put all the variables of a module into one global array. The array encapsulates all my data and, likewise, there's only one name that you need to worry about collisions for, e.g. ::mymodule(var) versus ::mymodule::var. As an aside, namespaces help but don't really solve the name collision problem; for example, in A Symmetric Doodler the (non-namespace) name symdoodle'move probably has the same probability of collision as the (namespace) name symdoodle::move would have.

A global array has further benefits. Encapsulation is in fact tighter w/ an array. It's much easier saving and restoring a modules state with array get and set. My rc files are often not much more than a prettied up copy of the output of array get (and to read them I just source them). Emulating the (very desirable I think) feature of class instantiation (w/ each instance having separate variables) is easier w/ arrays.

Granted, you could argue why not combine both techniques and get the best of both worlds. You can and I often do just that.

But where variable drives me crazy is in debugging. I debug by running my app from a console window, then I walk code by pasting in the code from the procedure I'm interested in debugging. This has served me well for over 10 years of tcl coding. But namespace variables break this, since these variables are not directly accessible from the console's default namespace. You either have to tweak each variable to use its fully qualified name, or to upvar the variables into the global namespace.

DGP: This is a good observation, but I think I draw a different conclusion from it. I don't see evidence that global is superior to variable. I see evidence that the Tk console is an inadequate debugging environment. By all means, do whatever works for you, but other readers might want to consider use of TkCon, or other more advanced debugging tools that overcome the observed weaknesses of the bare console. KPV: just to set the record straight, TkCon is the console I use, much better than the standard console. DGP My apologies if I'm off the mark. I thought that TkCon supported setting the namespace in which interactive commands are to be evaluated. JH Yes, TkCon supports an attach to namespace concept, which evaluates everything in the context of that namespace. However, that may confuse beginners more than help (but it is useful for those who know what they are doing). KPV: cool, I didn't know. I couldn't find anything in the doc about it but finally saw it on on the Console menu. Is there anyway of invoking this from the command line?

GPS: I don't agree with the variable vs. global issue either. In the past I used namespaces but now I find them awkward. In my SDynObject system I don't use namespaces. I don't have to worry about variable or procedure/method conflicts anymore than if I used namespaces, because of my object system. However I do think that using an excessive amount of globals makes a program inflexible, and the code difficult to reuse.

NEM: The key point here is that an array is a type of namespace - but for variables only. The big win of real namespaces is being able to partition command names as well as variable names. That, and you can't nest arrays, but you can nest namespaces.

RHS Note that, if performance is an issue, its faster to access scalars than it is array elements (at least, in my experience):

 % proc joe::p3 {} { global arr ; for {set i 0} {$i <1000} {incr i} { set arr(key) } }
 % proc joe::p4 {} { variable key ; for {set i 0} {$i <1000} {incr i} { set key } }
 % set arr(key) 1 ; set joe::key 1
 % time { joe::p3 } 1000
 3134 microseconds per iteration
 % time { joe::p4 } 1000
 1975 microseconds per iteration

Arts and crafts of Tcl-Tk programming