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)?
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
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.
[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
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
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
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
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
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)
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.