global fails

I noticed that the global I was using earlier didn’t work. I can easily work around this by messaging the local variable in the handler, but is this construction known to not be valid? I’m gathering that you can’t obtain a loop’s variable?

This is the gist:

global CounterVar

tell application "Finder"
	repeat with CounterVar from 1 to 3
		tell me to doSomething()
	end repeat
end tell

on doSomething()
	return CounterVar
end doSomething

I wouldn’t say, “global fails”. Consider this:

global CounterVar
set CounterVar to "example"

tell application "Finder"
	repeat with CounterVar from 1 to 3
		tell me to doSomething()
	end repeat
end tell

on doSomething()
	return CounterVar
end doSomething

CounterVar is defined and the scripts runs without error, though the output is differrent from what was expected.

Your script raises an error with the message, “The variable CounterVar is not defined.” From my script above, we know that the script above that your script would work if CounterVar was defined. That variable should be defined at the being of each loop, so the actual problem is probably related to a combination of repeat loop variables and searching for a variable outside of a handler.

That last paragraph could probably be written better, but I’m not going to waste any more time on it; While I don’t want to brush off any possible bugs here, the whole point would be moot if you just passed the variable as a parameter:

repeat with i from 1 to 3
	example(i)
end repeat

on example(i)
	return i
end example

While I understand that I can pass the data directly in the handler, I just wanted to see if it was technically incorrect to do as-is. I’m using another global declaration in my actual code (that doesn’t fail to globalize) and I wanted the two to be internally consistent - they either both get defined as globals or both get passed in the handler. Thanks anyway.

An interesting thing. A global variable loses its globality inside the doSomething() handler, if you pass it a repeat loop variable as a parameter:
 

global CounterVar
set CounterVar to "example"

tell application "Finder"
	repeat with CounterVar from 1 to 3
		log my doSomething(CounterVar)
	end repeat
	log my doOtherStaff()
end tell

doOtherStaff()

on doSomething(CounterVar)
	-- return local values
	return CounterVar -- add "of me" to get broken global's value instead
end doSomething
--> {1,2,3}

on doOtherStaff()
	return CounterVar
end doOtherStaff
--> "example"

 
But this is very bad news. The test I created shows that AppleScript can be spoofed, potentially giving erroneous results.

Hi KniazidisR.

The global declaration here is effectively above the implicit run handler. The first line in the run handler itself is the one that sets the global’s value to “example”.

Then there’s a repeat in which a variable of the same name is used as a repeat variable. This variable is local to the run handler, effectively overriding the global’s scope there. If you insert log CounterVar before the log my doOtherStaff() line, it’ll produce 3, while the log my doOtherStaff() line will still produce “example” because that’s the value of the global.

Similarly, in the doSomething() handler, you’ve declared CounterVar as the name of the parameter variable, which is local to that handler. So if you want to access the global, you have to add “my” or “of me” to indicate that this is what you mean.

This is all demonstrated if you make the run handler explicit. The results are exactly the same:

global CounterVar

on run
	set CounterVar to "example"
	
	tell application "Finder"
		repeat with CounterVar from 1 to 3
			log my doSomething(CounterVar)
		end repeat
		log CounterVar
		log my doOtherStaff()
	end tell
	
	doOtherStaff()
end run

on doSomething(CounterVar)
	-- return local values
	return CounterVar -- add "of me" to get broken global's value instead
end doSomething
--> {1,2,3}

on doOtherStaff()
	return CounterVar
end doOtherStaff
--> "example"

But if you put the global declaration inside run handler, the doOtherStaff() handler will error because it doesn’t have a CounterVar variable of its own defined and can’t see the global. However, if you declare the CounterVar global there too, the two handlers will share the global version, which is otherwise invisible in the rest of the script.

Hope this all makes sense! :slight_smile:

this topic has just been discussed here:

as @alldritt explains, the variable generated in the repeat with block is always local - even if you declared an identically named global variable (as far as I understand).

(it’s possible it was already mentioned in other replies.)

Thanks for the clarification to both users. I imagined the situation with the repeat loop variable in much the same way. This is a specialized variable, and AppleScript applies some special rules to it.

The bad news is that this can create a hard-to-find bug situation in a complex script. Well, I’ll consider this another reason to be as careful as possible when using global variables.