Using set variable vs. global vs. property

More info in the FAQ: http://bbs.applescript.net/viewforum.php?id=23

Thanks JJ,

I did not realise that a global variable could retain its value after running. Hummm. . . I could see where that could cause a lot of problems and head scratching if you don’t know this and don’t reset the variable every time. :cool:

PreTech

You’ve given a very good explanation, PreTech. :slight_smile:

I see the thread’s moved on a little while I’ve been writing this, but I may as well go ahead and post it. I enjoy a little expound now and then. :wink:

Properties of scripts are created and set to their initial values when the scripts are compiled. If a property value is declared indirectly as executable code ” such as ‘path to desktop as text’ ” the compiler runs the code and initialises the property to the result. The code isn’t run by the script itself, so on another machine, the property’s value will still be that obtained on the compiling machine. However, it’s possible to change property values at run-time, using the ‘set’ command. Whether or not the values are changed, properties and their final values are saved back into the script file when the script finishes running and the properties will have those values the next time the script’s run. (This isn’t reliable in Tiger, especially if you’re running the script from Script Menu.) Properties are global in scope to the scripts in which they’re declared.

Other sorts of variables come into existence the first time they’re given a value when the script runs. It’s a rather complex situation and I find it helps to divide them primarily into “top level” variables (ie. variables that are first set in the implicit or explicit ‘run’ handler) and variables that are used in handlers.

If a “top level” variable is declared as global, it’s accessible from anywhere in the script ” except of course in areas where it’s explicitly been declared local. If it’s declared as local, its scope is confined to the ‘run’ handler. If it’s simply used without its scope being declared at all, it’s a little of both! In this case, its scope is normally confined to the top level of the script; but if it’s declared as global in any of the other handlers, it’ll be common to both the ‘run’ handler and the handler(s) containing the global declaration(s). But it won’t be accessible from any other handlers where it’s not explicitly declared as global.

Variables in a handler are local to that handler unless declared otherwise, either in the handler itself or at the top level of the script. If they’re declared global in the handler but not at the top level, they can only be shared with “top level” variables of the same names and/or with similarly named variables that have been declared global in other handlers. (So its possible to have variables that are “global” within just a small group of handlers.)

Like properties, globals and “top level” variables (except those explicitly declared local) and their values are saved back into the script file when the script finishes. Thus they’ll already exist the next time the script is run. There doesn’t appear to be any practical use for this, but it’s useful to know that it happens. People have often been caught out because the final value of one of their “top level” variables has been something bulky (like a large text) and the system’s choked while trying to save it back into the script file. Local variables, on the other hand, cease to exist the moment the handler they’re in exits. What happens to their values depends on whether or not these have been passed to globals first or returned by the handler.

The properties of a script object ” as opposed to those of a compiled script ” are created and set only when the script object itself comes into being during the course of a run. They persist as long as the script object remains in existence as a piece of data. They’re not saved back into the script file unless the object’s been assigned to a persistent variable in the compiled script.

As I go farther and farther towards the deep end of this pool we call AppleScript, I want to get into good habits and conventions.

Today, egads!, I’ve had some time to actually do some reading on MacScripters and the two reference books my employer let me buy (“AppleScript: The Definitive Guide” by Matt Neuburg, and “AppleScript” by Hanann Rosenthal).

More often than not it’s “Shock Development”…I get interrupted alot, too many hats, and so “planning” sometimes takes a back seat to just feverishly writing code before the next interruption. Slowly I’m learning to write more handlers and less “megascripts” and try to get more time to actually make quicky flowcharts to figure out at least a few of the handlers ahead of time rather than while I’m writing. :wink:

One thing I’m still a bit confused on is the property attribute, and I haven’t used them outside of access properties for other files and applications.

Coming from years of programming (BASIC, C, Pascal, etc.) I didn’t have a problem wrapping my head around local and global variables, and the basics of parent-child instances of variables, and knowing when I stupidly stepped on my own toes in those regards.

But how do I know when I should be using a property versus a Global variable, since they seem very similar in behavior? Is it proper “form” to use one rather than the other for certain situations? I’ve been pretty much using globals declared in the implicit run handler, top of the script.

I also ask this from the perspective of writing code to be moved to FaceSpan (or AS Studio), if that makes any difference. I’ve already bumped into a few nuances where a perfectly running script does not always mean a perfectly running Project, and I’d like to set-up the habits to avoid those issues.

Also, my plug for Nigel, Adam, waltr, PreTech and others who seem to solve these problems I run into with relative ease. I’m outa practice with programming. :wink:

All right, what’s the deal with this?


set a to "test"
set c to b(a)

on b(a)
	global a
	-- set d to ...
	--
	return d
end b

Syntax Error: “Can’t declare a as both a local and global variable.”

It doesn’t like the handler’s global variable a as the argument to the same handler, b( ). Evidently a variable is declared local within a handler argument, no matter what. Even adding a global a to the top level implicit run handler doesn’t please AppleScript. It only successfully compiles if the global a is solely declared at the top level. Easy enough to solve the immediate problem, but, hey, I gots to know why!

AppleScript: 2.1.2
Operating System: Mac OS X (10.6.8)

You declare a global variable in the file to instruct the compiler how to handle the variable further in the script. Therefore you don’t declare globals inside the handlers.

The explicit local variable is to make sure that the class (blueprint of the object) is compiled in such a way that it doesn’t make a call to any global variable is exists. It’s not true that variables in handlers are local by default. A better term is that they’re greedy for being global. When it can be global it will be global otherwise just local, so to make sure it is local you can explicitly declare them as local as the following example proves:

global a

set a to 1
handlerWithoutLocal()
log a
handlerWithLocal()
log a
handlerWithoutLocal()
log a

on handlerWithLocal()
	local a
	set a to 5
end handlerWithLocal

on handlerWithoutLocal()
	set a to 10
end handlerWithoutLocal

As expected the the log result will show 3 times the value 10.

That’s a bit misleading ” or at least confusing. Variables in handlers are local by default. They’re only global if they’ve been declared thus in a scope which includes that handler. So if you declare ‘a’ to be global at the top of the script, it’ll be global inside the handler as well. The exception to this is if you use ‘a’ as a parameter variable as well, in which case it’ll be local inside the handler:

global a
set a to 10

aHandler(5)
return a --> 10

on aHandler(a)
	set a to a + 1
end aHandler

But you can still use the global by putting ‘my’ in front of it, since globals, like properties, “belong” to the script:

global a
set a to 10

aHandler(5)
return a --> 6

on aHandler(a)
	set my a to a + 1
end aHandler

You can also declare globals inside handlers, in which case they’re global between the handlers in which they’re declared and the ‘run’ handler:

set a to 10

handler1()
handler2()
log a --> 13
handler3() --> error: The variable a is not defined.

on handler1()
	global a
	
	set a to a + 1
end handler1

on handler2()
	global a
	
	set a to a + 2
end handler2

on handler3()
	set a to a + 5
end handler3

What you can’t do, as Schmye found, is declare a global inside a handler where the variable name already belongs to a local. In Schmye’s case, the local is the parameter variable, but the compiler will also complain if the name’s used in the handler before the global declaration:

on aHandler()
	set a to  5
	global a
end handler3

Because an parameter is by definition local when copied and passed trough the stack. You cannot declare global parameters so the compiler will handle the variable always as local with no exception. Maybe greedy is not the right word (I have no doubt your English is way better than mine :cool: ) but AppleScript will use global when it possible, so I thought that “default” is not the right term either.

So the take-away to my direct question is that I get the error because:

  1. A variable is auto-declared local in a handler unless a global declaration precedes it.
  2. My handler argument variable (I guess I should be calling it “parameter”) was local because the argument (parameter) preceded my global declaration in the handler, notwithstanding it being the very first line within the handler.

But then why did I get the error also when I additionally had a global declaration as the first line of the top-level run handler, which preceded the handler call? It sounds like I was right the first time, surmising:

…because as:

So the only reason that the variable set in the run handler is recognized by the subhandler is solely that it was passed to it because it was the argument (parameter), and the global declaration in the run handler was otherwise unnecessary. Right?

It’s possibly a little ambiguous. Understanding “default” to mean “a preselected option adopted by a computer program or other mechanism when no alternative is specified by the user or programmer”

[quote from Oxford Dictionary of English (British English) in Apple’s Dictionary.app]
, variables in handlers are local unless explicitly declared global or properties at the top of the script. If, on the other hand, you start with the existence of global or property declarations at the top of a script, those variables are global in handlers too unless explicitly declared local in individual handlers.

More accurately, it’s the value (or a pointer to it in memory) that’s passed. The parameter variable that receives it in the handler is local to the handler and isn’t the one declared global at the top of the script, even though they may have the same name and contain the same value. In that case, the global declaration would have been unnecessary, yes. On the other hand, as one of my examples above shows, changing the value of one of the variables doesn’t change the value of the other, so if you want the effect to be visible outside the handler, you have to code accordingly. Either:

  1. Declare a global and use a different name for the local in the handler; or
  2. Declare a global, use the same name for the local, and address the global specifically by putting ‘my’ in front of the variable name; or
  3. Don’t use a global at all but return a value from the handler and set the external variable to the result. This is usually the best idea.

Hi everyone! I hope I can revive this post.

Can someone explain why LIST variables behave differently than REGULAR variables in this regard?

Below see two examples. In the first one, the LIST variable is affected by the handler without being a GLOBAL variable. The second example behaves as expected. How can I avoid the first example from happening?

set myList to {"AA", "BB", "CC"}
sampleHandler(myList)
item 2 of myList -->> RETURNS "XX"
--
on sampleHandler(aTemporalList)
	set item 2 of aTemporalList to "XX"
end sampleHandler
set myVariable to "AA"
sampleHandler(myVariable)
myVariable -->> RETURNS "AA"
--
on sampleHandler(aTemporalVariable)
	set aTemporalVariable to "XX"
end sampleHandler

Model: iMac
AppleScript: 2.10 (194)
Operating System: macOS 10.13

When you set one variable to another, or pass a variable as a parameter to a handler, both the original and receiving variables contain the same object. In your first example, you’re changing the contents of the list which is that object. In the second, you’re changing the contents of the receiving variable to a different object.

Lists, records, and dates are sometimes called “shared data” when they’re contained by two different variables.

Hi NG! Thanks for your reply. I understand the concept of an object in memory that could be referenced by different variables but how can I just “copy” the LIST to a new variable instead of referencing that object in memory? Is it possible?

I just realized that we can take the handler out of the equation and get the same result. Here are both examples without the handler.

set myList to {"AA", "BB", "CC"}
set aTemporalList to myList
set item 2 of aTemporalList to "XX"
item 2 of myList -->> RETURNS "XX"
set myVariable to "AA"
set aTemporalVariable to myVariable
set aTemporalVariable to "XX"
myVariable -->> RETURNS "AA"

To avoid referencing 2 variables to same object in memory you should do 1 thing:

instead of set Variable_2 to Variable_1
use copy Variable_1 to Variable_2

The standard copy command is mostly used as an AppleScript command, although it can also operate as various application commands under some conditions. Like the set command, AppleScript’s copy command is commonly used to assign values to variables. Here is its syntax:
copy some_expression to variable_or_reference
There is one important difference between using set and copy, however. Although set always puts the original value you give it directly into the variable, when used with certain classes of AppleScript values, the copy command makes an identical copy of the original value and puts this in the variable instead.
This duplicating behavior applies only to values that can have editable properties and elements: lists, records, dates, script objects, and AppleScript references created with the a reference to operator. Simpler values such as numbers and strings aren’t affected. For example, the following,

set variable_1 to "John"
set variable_2 to variable_1

and the following,

set variable_1 to "John"
copy variable_1 to variable_2

do the same thing. The first line assigns a string value, “John”, to variable_1. The second line assigns the value of variable_1 to variable_2. Both variables contain the same object.
Similarly, if you use the set command with a list object, like this,

set variable_1 to {"John", "Paul", "George", "Pete"}
set variable_2 to variable_1

both variables contain the same object.

You can check this by changing one of the items in the list as follows:

set variable_1 to {"John", "Paul", "George", "Pete"}
set variable_2 to variable_1
set last item of variable_2 to "Ringo"

If you look at the value of variable_2, it’s just what you’d expect it to be:

variable_2
--> {"John", "Paul", "George", "Ringo"}

If you next check the value of variable_1, you’ll find it’s the same list object:

variable_1
--> {"John", "Paul", "George", "Ringo"}

This ability to put the same value into multiple variables can be useful in some situations, although it can also easily catch you out if you’re not careful! For example, what if you wanted to keep your original list around so you could use it again later in a different part of the script? Obviously, using set is no good for this.
One solution is to make a perfect copy of the original list by using the copy command instead:

set variable_1 to {"John", "Paul", "George", "Pete"}
copy variable_1 to variable_2

Now each variable contains a different list object. At first, they still look identical,

variable_1
--> {"John", "Paul", "George", "Pete"}

variable_2
--> {"John", "Paul", "George", "Pete"}

but you can now safely alter the contents of the second list without affecting the first one:

set last item of variable_2 to "Ringo"

variable_2
--> {"John", "Paul", "George", "Ringo"} -- Our new list

variable_1
--> {"John", "Paul", "George", "Pete"} -- Our original list

Thanks, KniazidisR! That answers my question!! I’ve used the COPY keyword in the past for copying files to a new location or for adding an item to the end of a list but never in that manner. So now I know…

For everyone else’s reference here is the working code:

set myList to {"AA", "BB", "CC"}
--set aTemporalList to myList
copy myList to aTemporalList
set item 2 of aTemporalList to "XX"
item 2 of myList -->> RETURNS "BB"

It’s still a bit confusing why the “list object” behaves differently than a “single value object” when using the SET keyword in AppleScript. This second example works the same using COPY or SET

set myVariable to "AA"
--set aTemporalVariable to myVariable
copy myVariable to aTemporalVariable
set aTemporalVariable to "XX"
myVariable -->> RETURNS "AA"

It’s unclear to me how to make myVariable and aTemporalVariable reference the same value in memory. I can’t think of any practical uses for that but would be interesting to know, if anyone has the answer :smiley:

You can create few instances (that is, variables) of same object in memory, then kill some of variables and leave others. That gives you ability to manage memory (init, release), used by script, similar to C or Swift. One user here asked help to play VLC (that is, 1 same object) on 4 screens… :slight_smile:

As KniazidisR says, you can use copy, which duplicates the contents of the source variable (if it’s a list, record, date, or other “structure” object) to the receiving variable. It’s a “deep” copy, so if any of the object’s contents are lists, records, or dates, they’re duplicated too.

If the object in the source variable’s a list, it can sometimes be useful to set the receiving variable to the source’s items instead:

set Variable_1 to Variable_2's items

In this case, Variable_1 is a different list object from Variable_2, but containing the same items — just as like both variables initially containing the same lists in the earlier discussion. So you can change the items in one list without affecting the other, but if any of the items they both contain are lists, records, or dates, changing their contents will be visible in both lists!

If a handler’s expecting a “structure” object as a parameter, and you want to change its contents in the handler without affecting the original outside the handler, you can simply copy it at the top of the handler. In fact, since the parameter variable is local to the handler, you can even copy it to itself!

on aHandler(myList)
	copy myList to myList -- Set myList to a duplicate of what it already contains.

	-- etc.
end aHandler

You should use duplicate for that. :slight_smile:

It’s more efficient to use set in that case, unless you definitely need the added item to be a duplicate of an original.

To understand what is one object in memory - simply byte sequence. The variable is address to first byte of that sequence

Guys, this is great stuff, I’m going to chew this up tomorrow morning at my best.

I thought Swift had automatic memory management (garbage collection…?) I remember it from when it was released, along with advertised simplicity and the removal of some “annoyingness” of common programming languages. I would like to learn Swift but I was recommended to get into .NET better since it is open to more platforms. What would you recommend??? :smiley:

My longest AppleScript script is close to 2000 lines, I’m probably pushing the limits of it (or not optimizing my code enough LOL) but I find it simple and useful. I like how it integrates with other programs like the Finder, Adobe apps, Excel, etc., and even the use of shell scripts seals the gap of missing tools. I have few scripts that run on a schedule fetching info from the internet, moving files and folders around and saving me a bunch of time on daily tasks. I just counted 335 script files in my Script Editor folder LOL With that said, I think its time for me to learn how to build standalone apps :slight_smile:

The question raised by sergiorbp has been answered, but I thought I might add a quick note. In discussing this broad issue, the AppleScript Language Guide uses the term mutable, and, simply stated, mutable objects (such as a list) can share data but nonmutable objects (such as a text string) cannot. Thus, the language guide states:

“If multiple variables refer to a mutable object…, changes to the object are observable through any of the variables [emphasis mine].”

For the reason noted above, this is not directly possible, but it can be accomplished by making the text object into a mutable object:

set myVariable to {"AA"}
set aTemporalVariable to myVariable
set item 1 of aTemporalVariable to "XX"
item 1 of myVariable -->> retunrs "XX"