Variable Scope Problem

I have a problem with the scope of one of my variables in a script

This is a simplified script follows

I initiate my script like this:

set noLoops to 0

I then give the variable a value like this:

tell application "Safari"
	try
		tell document 1 to repeat
			set noLoops to noLoops + 1
        end repeat
        my extractEvents()
	on error errMsg
		log "errMsg: " & errMsg
	end try	
end tell

Note that I called the extractEvents method above.

When I then try to use noLoops in this method:

on extractEvents()
	tell application "Safari"
		tell document 1
			set tsv to tsv & "ec: " & eventCount & "	" & "loops: " & noLoops & "	" & "e/l: " & (eventCount / noLoops)
		end tell
	end tell

I get an error message:

(*errMsg: The variable noLoops is not defined.*)

How can it be not defined when I declared it in the global scope (yeah yeah, you shouldn’t use global variables)?

It’s not a global unless you declare it a global

global noloops

You could also send the local variable noloops to ‘extractEvents(noloops)’

So, first, this repeat loop has no exit. As written this script would go into an infinite loop until the value of the noloops variable exceeded the highest possible value.

As to your question, the scope of AppleScript variables is a little complex.

Properties are truly global. You declare them once at the beginning of your script and they’re available to every handler.

Globals are declared as global in every handler you use them in (except the implicit run handler).

In the case of your script, you may want to simply send the value of your variable noLoops to the handler as a local variable, as below.

set noLoops to 0

tell application "Safari"
	try
		tell document 1
			repeat 10 times
				set noLoops to noLoops + 1
			end repeat
			my extractEvents(noLoops)
		end tell
	on error errMsg
		log "errMsg: " & errMsg
	end try
	
end tell


on extractEvents(noLoops)
	tell application "Safari"
		tell document 1
			set tsv to tsv & "ec: " & eventCount & "	" & "loops: " & noLoops & "	" & "e/l: " & (eventCount / noLoops)
		end tell
	end tell
end extractEvents

I’ve never had to declare a global in every handler. To me they always worked just like properties other than not being initialized at the beginning like properties

To elaborate a little on @estockly’s reply:

Properties are global within the script in which they’re declared — except that in child or contained script objects which declare the same properties, the child scripts’ properties are not those of the parent.

Variables that are declared global at the top of a script are global throughout the script, except in handlers where they’re declared local or are parameter variables to those handlers. Alternatively, globals can be declared within individual handlers and they’ll only be shared between the handlers in which they’re declared.

Addenda:

It’s possible inside a handler to disinguish between locals and globals with the same name by putting the word my in front of the name when you mean the global.

With properties, it’s a bit more subtle. my inside a handler refers a property of the script from which the handler’s called:

property fred : "Hello"

on aHandler()
	return my fred
end aHandler

script o
	property fred : "Goodbye"
	
	on saySomething()
		say aHandler()
	end saySomething
end script

say aHandler() -- aHandler() called from main script.
tell o to saySomething() -- aHandler() called from script object o.
tell o to say aHandler() -- Ditto.

You can experiment by omitting the my from aHandler() and/or the property declaration from the script object to get an idea of what happens in any particular situation.

3 Likes

What is the reach/scope of a variable that is declared on the “root” level like in my example if it is not global?

Local

Unless specifically declared global.
You also might want to read up on the “run” handler and also an implicit “run” handler

[quote=“lagr, post:6, topic:74606, full:true”]
What is the reach/scope of a variable that is declared on the “root” level like in my example if it is not global?
[/quote ]

global A -- available to "on run" handler and all other root script handlers.
-- If it is not declared at the top of the root script, then it is local 
-- (only available to the "on run" handler)

on run -- this code line and "end run" may be invisible, but "on run" handler exists
	set A to "hello"
	sayHello()
end run -- 

on sayHello()
	say A
end sayHello

 
Here, if you do not declare variable A global, its value in the “on run” handler will become = “hello”, but it will not be spoken aloud by another handler. You will get an error.

It worked for me. I opened your script and ran it without changing it.

NEVERMIND: I see what you’re saying, that if you don’t declare it at the top as you do in the sample it won’t work.

You are correct. If the global is declared at the top of the script it’s available throughout.

Another method is to declare it in the run handler, and declare it in each handler where it is used. If you try to use it in a handler where it’s not declared you get an error

on run 
	global A
	set A to "hello"
	sayHello()
	sayHelloNoGlobal()
	
end run -- 

on sayHello()
	global A
	say A
end sayHello

on sayHelloNoGlobal()
	say A --fails
end sayHelloNoGlobal


And, if you declare the global in the implicit run handler (without the on run/end run) it is available even where not declared.

--on run 
global A
set A to "hello"
sayHello()
sayHelloNoGlobal()

--end run -- 

on sayHello()
	global A
	say A
end sayHello

on sayHelloNoGlobal()
	say A
end sayHelloNoGlobal

With implicit “on run” handler:

This is how you think that you placed the declaration of a global variable inside an implicit on run handler. In fact, in this case, the declaration is at the top of the root script, outside the handler.

You can put comments anywhere, even code lines of “on run” handler can be placed anywhere except another handler. Whatever you think, the interpreter will bring your script to a structure equivalent to my script. My script gives users an explicit idea of what the interpreter is doing. That’s why I posted my “explicit” script.

Properties and “use” declarations available anywere also go to the top of root script

Hmm, but what about this situation

global A
set A to "hello"
sayHello()
sayHelloNoGlobal()


on sayHello()
	global A
	say A
end sayHello

on sayHelloNoGlobal()
	say A
end sayHelloNoGlobal

say "Goodbye"

Isn’t sayHello and sayHelloNoGlobal inside the implicit run here? Or how is the last say "Goodbye" interpreted with respect to structure?

I thought I already explained. Well, one more time. Your script, which you see with your eyes, is interpreted by the interpreter in the background as the following script:
 

global A

on run
	set A to "hello"
	sayHello()
	sayHelloNoGlobal()
	say "Goodbye"
end run

on sayHello()
	global A
	say A
end sayHello

on sayHelloNoGlobal()
	say A
end sayHelloNoGlobal

 

For clarity, the handler calls

sayHello()
sayHellNoGlobal()

Are in the implicit run handler.

The handlers themselves are not in the implicit run handler. They are their own handlers and seen as separate handlers by the interpreter


on SayHello()
---
end sayHello
on SayHelloNoGLobal()
---
end SayHelloNoGLobal

You missed my last line. That makes all the difference.

The AppleScript Language Guide defines an implicit run handler as “all statements declared outside any handler or nested script object in a script.” So, the last line of your script is the last statement in the implicit run handler. Running your script verifies that this is the case.

BTW, an explicit run handler can only be specified once, so your script could not be written as it is but with an explicit run handler. In that case, the script would be written as posted by KniazidisR :

global A

on run
	set A to "hello"
	sayHello()
	sayHelloNoGlobal()
	say "Goodbye"
end run

on sayHello()
	global A -- no need for this is this instance
	say A
end sayHello

on sayHelloNoGlobal()
	say A
end sayHelloNoGlobal
2 Likes

Yes, but remember the global and property declarations, as well as the use declarations are not considered statements and are not considered part of the implicit run handler.

The biggest difference between the way an explicit run handler and implicit run handler work is how they scope undeclared variables.

In the explicit run handler, global variables must be declared to be accessed in any other handler.

In the implicit handler all variables are treated as global, whether they are declared or not, and available to other handlers when they are declared.

set A to "hello"
sayHello()
sayHelloNoGlobal()
say "Goodbye"

on sayHello()
	global A
	say A
end sayHello

on sayHelloNoGlobal()
	say A
end sayHelloNoGlobal

Global. But it must be declared global in each handler it’s called in. See my example above.

You both amaze me already. What do you understand by the expression "root level? For example, I mean by this the root script (anonymus object «script» for accuracy).

I think this is logical, because first the script instructions in the top are executed, and then the commands of the “on run” handler, and then other handlers.

If you think that “global”, “property” and “use” declarations at the top are not commands, you are deeply mistaken. These are interpretator macros that are executed first.

Also, you can encapsulate (isolate) global declarations at the top of a nested script. Then they will be global within the nested script, but inaccessible to the parent script.

“Root level” is not a term used in the AppleScript language guide. You’re referring to any part of the script not inside a declared handler (including an explicit run handler).

The AppleScript language guide specifies that the declarations at the top of the script are not considered statements. (I believe this part of the ASLG may have been written before “use” declarations were implemented but their scope is always global)

About Handlers

When you run a script or launch a script application, its run handler is invoked. A script’s run handler is defined in one of two ways:

As an implicit run handler, which consists of all statements declared outside any handler or nested script object in a script.
Declarations for properties and global variables are not considered statements in this context—that is, they are not considered to be part of an implicit run handler.

As an explicit run handler, which is enclosed within on run and end statements, similar to other handlers.
Having both an implicit and an explicit run handler is not allowed, and causes a syntax error during compilation.

Humans tend to make mistakes. I also make mistakes sometimes. Then I admit my mistake, but as I see it, you do not want to do this. The discussion became pointless.