Re: Scope of globals, parents and children, "load script" and all that
Re: Scope of globals, parents and children, "load script" and all that
- Subject: Re: Scope of globals, parents and children, "load script" and all that
- From: Brennan Young <email@hidden>
- Date: Sun, 20 May 2001 18:58:35 +0200
- Organization: Magic Lantern
This is a very interesting thread.
Here's how I understand it.
If A is a script stored in an external file, and B (also a script) loads the
script object into itself, we might expect A to suddenly see all of B's globals.
Global means 'seen everywhere' or else it means nothing at all.
I believe this is Sander's beef, and I can follow him on this.
The way I see it though, scripts are compiled seperately, so there's no way that
the compiler of A can know that B has a global with a given name, so it will
complain about that.
The reason you are obliged to declare globals in both places, then, is because
the compiler needs to connect the variables to the same value in both objects.
There is no way that it can do this unless some kind of preprocessor is involved
- and Applescript has none.
Additionally, Applescript has static data stored in the scripts, but not
actually part of the source code, which would make it very difficult to prevent
globals in one script from trampling on unrelated but eponymous globals in
another script that you happened to compile and/or run in the same session.
In languages like C, you can declare globals in one place and then use them
everywhere because a preprocessor and linker takes care of 'duplicating the
declaration' whereever the global is used. C programs get compiled and linked at
the same time before they are run. In languages with no preprocessor and no
linker - i.e. most scripting languages - this is not possible.
As Victor says, 'Globals aren't "bad"', but they are messy, and apart from all
the name-space conflicts that they can cause, they require the programmer to
mentally keep track of them - which is a dreadful waste of any programmer's most
valuable resource: attention span. In other words, they're not bad, but it's
almost always better to use another technique if you can.
I would argue that most people use globals out of ignorance and/or bloody minded
laziness. If you're truly lazy (an essential characteristic of a good programmer
IMHO), you don't use globals because the time they may save at the start of a
project is more than lost as the project grows in complexity, and in direct
relation to that complexity.
My approach is not to use globals at all. Easier said than done? Well, it's a
matter of habits. I find it very easy. Here are a few heuristics. I can expand
upon them, and will gladly do so if there is interest;
If you find you need a global, try and find out which handlers would need to
access that variable and see whether you can't bundle them together into a
script object of which the 'global' can be a property. If you don't have any
handlers (shame on you) try and isolate the access to your globals into handlers
as a first step, then see whether you can't wrap these handlers further into a
script object.
If you find that you have a situation where more than one script object needs to
see the same data, try to find out when this is happening - i.e. how often is
that data being referred to. It's likely that only one object needs to 'own' the
data. The others can ask for it when they need it, or ask the object to do
something for them with its internal data.
You should be able to have the objects caching the data in a property or local
variable at initialisation time, or at the beginning of some procedure or
handler. That way, you can pass 'the data formally scoped as global' as a
parameter to an init() handler or something like that.
With datatypes that are passed by reference, rather than by value, you won't
even be caching the data, you just store a reference to it, so even if it is
dynamic, the object or handler will always use the most up-to-date value.
If you have a situation where data is very dynamic and still needs to be seen by
more than one object, you might make a script object to manage just that data,
and store a reference to the manager object everywhere you need it. Provide a
handler inside the manager object which allows outsiders to access the data,
with whatever security limits you want to impose (read only or whatever) or
(better still) get the manager object to take in other, more transient, data as
parameters and use it to act on the (previously global, but now internal) data directly.
I've been following this approach for several years now, and only ever need to
use globals when I really need a quick fix (like the client is coming in 5
minutes). I am honestly not exaggerating for the sake of making a point, but my
point is that you don't need to use globals at all.
Sure, you often need to think laterally about which object is really responsible
for which data, but it's a good habit to cultivate.
Using instances is another great way to think out of the box of using globals. I
suspect that many Applescripters are not using instances much, or at all. As
Sander puts it:
>
Hm... This sounds interesting. Made me dive into the "Script Objects" chapter
>
of the AppleScript Language Guide, which is mostly incomprehensible though :(
>
I did run into one specific thing though: on page 331, under "Definining
>
Inheritance", it says:
>
>
You specify inheritance with the Parent property. A script object that
>
includes a Parent property inherits the properties and handlers of the
>
script object listed in the Parent property.
>
>
That seems to say there is a way to make the child use the globals of the
>
parent, but I still don't see how to exactly use this. The Guide doesn't give
>
any examples :(
Well, a better way to think about is that the child inherits the *properties* of
the parent. Sure, you can get the globals too if you declare them in both
places, but I have just expounded about why that should not be necessary.
Try this:
Script 1
property a : ""
on test()
set d to display dialog "Test" default answer "Hello"
set a to the text returned of d
end test
Script 2 (in a seperate file)
property parent : (load script (choose file)) -- chose script 1 of course
on run()
test() -- this handler only exists in the ancestor
a -- so does this property. Look in the result window
end run
Notice that 'a' is declared as a property of script 1 (the ancestor or
'parent'), but it's being referred to in script 2 as though it is one of script
2's own variables or properties.
If more than one script uses script 1 as an ancestor, they can all share the
same data without having to declare it all over the place, as long as this data
is declared as a property of the ancestor. The same applies to any handlers
which might be located in Script 1.
>
Btw, isn't my child script (which is only a bunch of handlers in my current
>
version) an implicit "script object" (when I load it into the main script
>
through a "load script" command)?
Sure, but it's not inherited by the main script unless you use the parent
property - you'd still need to refer to the object returned by (load script) if
you wanted to use it.
Sander, what are you up to that you need 17 globals? Maybe we can help
reorganize it.
--
_____________
Brennan