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: Mon, 21 May 2001 03:44:56 +0200
- Organization: Magic Lantern
Sander Tekelenburg <email@hidden> wrote
>
Brennan Young wrote :
>
>
> 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.
>
>
Just to make sure I understand you correctly: with "ask" you mean something
>
like the following right?
>
>
script myObject
>
on DoStuff()
>
set Var1 to "hello"
>
set myName to get computer name
>
return {Var1, myName}
>
end DoStuff
>
end script
>
- --ask the above object for the values of its variables:
>
set {Var1, Var2} to DoStuff() of myObject
>
- -- pick the needed variable and use it:
>
display dialog Var2
>
>
Is this what you mean?
>
>
Is there another method/syntax possible? I would like to simply say something
>
like:
>
>
get myName of DoStuff() of myObject
Well, that syntax should work if myObject looked like this:
script myObject
on DoStuff()
return {var1 : "hello", myName : get computer name}
end DoStuff
end script
... but read on ...
>
Also: isn't the downside of using this approach instead of using globals,
>
that you repeatedly tell yopur script to do the same thing? I mean, of it is
>
declared to be a global, you only need to define its value once. Could affect
>
speed. Right?
Certainly, and that's where the next part of my post comes in...
>
> 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.
>
>
I don't understand what you're saying.
OK, check out this version of 'myObject'...
myObject's init() -- put this near the beginning of your script/procedure
display dialog myObject's computerName()
script myObject
property var1 : "hello" -- this can be set at compile time
property myName : "" -- this can't
on init() -- called at runtime
set myName to get computer name
end init
on computerName()
return myName
end computerName
end script
This is a bit long winded for such a simple example. You can also get the
property out directly like this:
myObject's myName
... which would appear to be simple, but is considered 'dirty' by OO purists
because it doesn't give the object a chance to notice that one of its properties
is being accessed. I'm inclined to agree with this, although in this simple
example it's not clear why this is important.
Consider that the data was only to be accessible with a password or an id, which
needed to be passed as a parameter to computerName(). That way, the object has
some built in security. It's not just a container for data, it also has some way
of protecting that data from snooping.
The best reason, though, is debugging and maintanence: If an object's data is
open to manipulation by unknown or unspecified external actors, it takes longer
to find where any problems are than if the object alone manipulates its own data.
>
> That way, you can pass 'the data formally scoped as global' as a
>
> parameter to an init() handler or something like that.
>
>
Don't you mean "formerly"? (If you really mean "formally" then I don't
>
understand what you're saying.)
Oops! Yes, I meant formerly.
(Ha ha. Now I'm thinking about the artist informally known as Prince instead of
all those silly pseudonyms).
>
> 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.
>
>
I don't understand this. What do you mean with "datatypes" and what with "by
>
reference" vs "by value"?
Simple types of data, like integers and floats are passed 'by value', which
gives us things like this:
set a to 5
set b to a
set a to 10
get b -- returns 5
All well and good, right? Now let's try with a list:
set l to {1, 2, 3}
set m to l -- two references to the same list now
set item 1 of l to 99 -- manipulating l...
get m -- returns {99, 2, 3} ... also affects m
When you assign a variable to a 'high level' type of data, such as a list, a
record or a script object, you don't get a copy of the data in the way you do
with an integer. Instead, you get a reference to the data, so you can manipulate
it from two (or more) places.
This can be a problem if you don't know about it, because it's perhaps more
intuitive to expect that all assignments create new, unique data. The 'copy'
command is designed to give us a unique copy of data which might otherwise be
passed 'by reference':
set l to {1, 2, 3}
copy l to m -- second copy of the original list
set item 1 of l to 99 -- does not affect m because it's a different list
get m -- returns {1, 2, 3}
So you can use objects which are passed by reference, such as lists, records or
script objects, to make data available to various places without making it global.
>
> 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,
>
>
Like this?
<snip> well, not quite. More like this:
myObject's init()
script myObject
property var1 : "hello" -- this can be set at compile time
property myName : "" -- this can't
property startUpDisk : "" -- this can't either
on init() -- called at runtime
set myName to get computer name
tell application "Finder"
set startUpDisk to name of startup disk
end tell
end init
on computerName()
return myName
end computerName
on startDisk()
return startUpDisk
end computerName
end script
I suppose I don't have to point out how unwieldy this seems, but what you get
here is a script object which you can (for example) pass around a network, which
remembers which computer it came from and what the startup disk on that computer
is. You could add some other handlers to it, so that it remembered some other
data about its 'home' computer, and was able to do some fancy stuff on that
computer when asked. That's a powerful thing!
>
> [...] 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.
>
>
Eh? :)
Hmm. I think I've condensed several years of object oriented explorations into a
sentence which is too small to contain them! :)
OK, in the above example, we might imagine that myObject moves around a network
and has ended up on the administrator's Mac.
Suppose myObject has another handler which is called by a script on the
administrator's mac, and takes in some data through a parameter supplied with
the call. It then writes some file to the prefs folder of the startup disk of
the computer it came from. The administrator script doesn't need to know which
mac myObject came from, it just says 'go back home and do your thing with this data'.
>
> 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.
>
>
I wouldn't know if I use them. What *is* an "instance"? :)
Now we're getting into some really heavy stuff. We only have one 'myObject'. It
contains data (stored in properties) and has some handlers, which is lovely, but
there often comes a time where you need to make copies of script objects which
have the same structure - the same set of handlers and properties - but have
different values for the properties.
To do this, you need to wrap the script object in a handler which returns the
structure. It's a bit weird, but this is the basic idea:
on run
set bill to x()
bill's init("William Gates")
set Maggie to x() -- a different instance!
Maggie's init("Margaret Thatcher")
bill's greeting()
Maggie's greeting()
end run
-- a constructor function contains a script object
-- and returns a new instance of the script
on x()
script z
property moniker : ""
property age : 0
on init(n)
set name to n
set age to (random number from 1 to 99)
end init
on greeting()
set greeting to "Hello, my name is " & moniker & " and my age is " & age
display dialog greeting
end greeting
end script
return z -- this is the magic part. It returns a unique copy of z!
end x
>
I think I understand this. But it seems to be the other way around from what
>
I thought I wanted to do. Here, the child loads the parent. I thought I
>
needed the parent to load the child...
Hmm. There's a lot of confusion around the terms 'parent' and 'child'. Sometimes
it seems to be backwards. I usually like to think that parents contain, own, or
take care of their children. Children are the properties of their parents. In
XML, for example, there is talk of 'child nodes', whose parent is the node that
contains it.
However, 'parent' is also commonly taken to mean 'ancestor' or 'superclass'. In
applescript, for example, if an object has a 'parent' property, it inherits data
and handlers from the parent, so it's the child which 'owns' the parent. Of
course, we carry around the DNA of our parents, so this is not such a strange
idea. It would be very strange to carry your parents around with you however.
In lingo (which is where I'm coming from) the syntax is similar, but the keyword
'ancestor' is used instead of the word 'parent' which is less confusing, because
it suggests inheritance rather than containment or ownership.
>
Concerning my real-wold script: what I mean with "parent" is an applet that
>
the user activates. The child is a separate script, containing some handlers,
>
which is loaded by the parent. Perhaps it is a mistake to consider the
>
user-launched applet to automatically have to be the parent? Could/should the
>
user-launched applet be the child, and load its parent (through "property
>
parent : load script [alias]")?
Absolutely! The beauty of object oriented designs is that by turning everything
upside down, elegant solutions often reveal themselves!
Even so, it's possible you might be able to do what you are about without using
inheritance at all. Inheritance is one way of allowing one object to access the
handlers and properties of another, but it's usually reserved for situations
where you need several kinds of object to share some characteristics, but also
to have their own unique attributes and abilities.
--
_____________
Brennan Young
Artist, Composer and Multimedia programmer
mailto:email@hidden
Hofstadters law:
"It always takes longer than you think, even when you consider Hofstadter's law."