10.7. Declare Your Variables
Sounds like "eat your vegetables," doesn't it? Well, it should. Each motto is good advice, no matter how unpalatable it may seem at first. I strongly advise you to declare all your variableseven (especially!) your locals. Unfortunately, AppleScript gives you no help with this; you can't make it warn you when a variable is undeclared. Rather, it silently declares it for you. This behavior, doubtless intended to make programming in AppleScript easier, is in fact the source of more mistakes and confusion among AppleScript programmers than any other feature of the language.
The trouble is that if you are lulled into a false sense of security because there's no need to declare your variables, then you can be surprised when some other scope tramples them. For example, imagine that your script starts like this:
set x to 5
That's top-level code, so you've just implicitly declared x a global. This means that any other handler or script object anywhere in this script can access your x and change its value, just by saying this:
This other code may not have intended to trample on your x; perhaps it was trying to establish a global of its own, possibly in order to communicate its value to code at a lower level, or to function as persistent storage between calls. But the damage is done, just because of the upward effects of a global declaration. And to think that you could have prevented this by declaring your top-level x local to start with.
The converse is also true. Pretend that you have a large script and that this code occurs somewhere within it:
on myHandler( ) set x to 5 end
Is x a local or a global here? You don't know! It depends upon the context. If x has previously been declared global at a higher level, this x is global (by the downward effect of that declaration). If not, this x is local. But it is intolerable that you should have to search elsewhere to learn the scope of x within myHandler!
A particularly tricky problem is presented by script objects loaded from a compiled script file on disk (see Chapter 8). For example:
set f to (path to desktop as string) & "lib.scpt" run (load script alias f) x -- "howdy"
Hokey smokes! How is that possible? Where did this variable x come from? It came from the script in the file lib.scpt, which simply says this:
set x to "howdy"
The x in lib.scpt is an implicit global, and when you load and run that script, the global infects your own script's namespace. This is extremely cool or extremely scary, depending on your point of view. It seems cool to be able to make a variable (which can equally be a script object or handler) magically appear, name and all, in your script. My own view, however, is that globals, implicit or explicit , are for this very reason to be avoided in scripts that will be loaded into other scripts. After all, if you didn't know that lib.scpt was going to do this, and your script had its own top-level x, then loading and running lib.scpt would make your script malfunction mysteriously.
The ideal way to manage scope, in my view, would be to declare everything. If you need a variable to be visible in this scope and deeper scopes, then declare a property. (In a handler, merely declaring a local will do.) Otherwise, declare a local. Use global declarations sparingly, only for special occasions when separate regions of scope need shared access to a value. If you follow these rules, then any undeclared variable must be a free variable (see the next section). Thus you always know what everything is. Unfortunately, as I've said, AppleScript doesn't help, so you can't easily and reliably follow these rules, even if you want to.
A local or global declaration, by the way, does not have to appear right at the top level of a scope block; it can be embedded in a control structure. However, it isn't an executable statement; it is seen and enforced by the compiler, not the runtime engine. In this example, 1 is not 2, so the local x declaration is never "executed," but the example shows that x is local anyway:
if 1 is 2 then local x end if set x to 7 on myHandler( ) global x display dialog x end myHandler myHandler( ) -- error: The variable x is not defined
That example is silly, but the ability to declare a variable within a control structure is not, because it encourages and helps you to (you guessed it) declare your variables. You could be deep in the middle of typing a complicated control structure when you suddenly introduce a new variable, and you realize that you want to declare it. You don't have to hunt back through your code looking for a place to declare the variable at the top level; you can declare it where you are and get on with your work.