[ Team LiB ] |
8.5 Scoping of HandlersA handler definition may appear only at the top level of a script or script object. (Therefore you can't nest handlers, except indirectly, by having a handler inside a script object inside a handler.) A handler is visible to code in the scope where it is defined, even code that precedes the definition (see Section 4.4.1). For example: myHandler( ) -- Howdy
on myHandler( )
display dialog "Howdy"
end myHandler
A handler is visible to scopes within the scope where it is defined. Remarkably, this works even before the handler is defined. For example: script x
myHandler( )
end script
run x -- Howdy
on myHandler( )
display dialog "Howdy"
end myHandler
A handler is visible on demand from outside the script object where it is defined. Code that can see a script object can refer to its handlers, using essentially the same two kinds of syntax by which it would refer to its properties (see Section 7.5.1). You don't need to use the word its to call a script object's handler from within a tell block addressed to that script object. This example illustrates both kinds of syntax: script x on myHandler( ) display dialog "Howdy" end myHandler end script x's myHandler( ) -- Howdy tell x myHandler( ) -- Howdy end tell The comparison between handlers and properties is apt. The scoping of handlers is very similar to the scoping of properties. They are both top-level entities of a script or script object; they are visible on demand wherever that script or script object is visible; and they are automatically visible globally downwards. 8.5.1 Handler Calls Within Script ObjectsA handler call within a script object is subject to a special rule: a handler is not callable from within a script object except by a name that is defined as a global within that script object or its inheritance chain. (The inheritance chain is explained under Section 9.7.) When I say "defined as a global" I mean that the name is the name of a handler, a property, or a global variable. This is not really a rule about scope; it's a rule about how handler calls work. But this is the place to mention it, because your main interest in a handler's scope will be for purposes of calling the handler. The rule actually has to do with the fact that a script object is an object to which one can send messages. A handler call is such a message, so it falls under the targeting rules for object messages. A message sent from within an object, if no explicit target is given, is assumed to be targeting that same object. If a script object can't deal with a message, AppleScript looks up its inheritance chain for a script object that can. Because of this rule, the following code generates a runtime error: script x on myHandler( ) display dialog "Howdy" end myHandler script y myHandler( ) end script run y end script run x -- error That's because the call to myHandler is in a script object y, and there is no explicit target, so the call is assumed to target the script object y; but myHandler is not a global defined in y. Now, if we move the definition of myHandler to top level, the code works just fine: on myHandler( )
display dialog "Howdy"
end myHandler
script x
script y
myHandler( )
end script
run y
end script
run x -- Howdy
That's because when myHandler is not found in y, we look up the inheritance chain, starting with y's parent. By default, the script as a whole is y's parent, and there we do indeed find myHandler as the name of a global (in particular, of a handler). To show that this rule is not about scoping, I'll prove that myHandler, when defined in x, is in fact visible from within y even though it is not directly callable from within y. The proof involves treating a handler as a value, which isn't discussed until later in this chapter, but you should be able to make sense of it: script x
on myHandler( )
display dialog "Howdy"
end myHandler
script y
property localHandler : myHandler
localHandler( )
end script
run y
end script
run x -- Howdy
The key point is that this line works: property localHandler : myHandler That suffices to show that myHandler is visible from within y. The example also shows how myHandler can be called from within y, by referring to it through a name that is defined within y. Another technique is to specify explicitly the target for the call: script x
on myHandler( )
display dialog "Howdy"
end myHandler
script y
x's myHandler( )
end script
run y
end script
run x -- Howdy
8.5.2 RecursionA handler is visible from within itself. This means that recursion is possible: a handler may call itself. Explaining the elegances and dangers of recursion is beyond the scope of this book. The best way to learn about recursion is to learn a language like Scheme or LISP, where recursion is the primary form of looping. In fact, in conjunction with lists, AppleScript's recursion allows some remarkably Scheme-like (or LISP-like) modes of expression (see Section 5.5).
For example, here's a recursive routine for filtering a list. We'll remove from the list everything that isn't a number: on numbersOnly(L)
if L = {} then return L
if {class of item 1 of L} is in {real, integer, number} then
return {item 1 of L} & numbersOnly(rest of L)
else
return numbersOnly(rest of L)
end if
end numbersOnly
numbersOnly({"hey", 1, "ho", 2, 3}) -- {1, 2, 3}
8.5.3 The Run HandlerEvery script has a run handler. When I say "every script," I mean either a script as a whole or a script object. When a script or script object is run, what runs is its run handler. This run handler may be implicit or explicit.
Here is a script with an implicit run handler: sayHowdy( ) on sayHowdy( ) display dialog "Howdy" end sayHowdy The executable statement that makes the run handler implicit is the first line, the handler call. The handler definition that follows is not an executable statement and is not relevant to the matter. Here is a script with an explicit run handler: on run sayHowdy( ) end run on sayHowdy( ) display dialog "Howdy" end sayHowdy You will observe that the on run statement lacks any parentheses. The run handler is special and doesn't use parentheses. The effect of running this second script is exactly the same as running the first script. To tell a script object to run its run handler (whether implicit or explicit), target the script object with the run command. This may be done using either of two syntaxes: the script object may be the run command's direct object, or the run command may occur in the context of a tell block targeting the script object. This example illustrates both approaches: script x on run display dialog "Howdy" end run end script run x -- Howdy tell x run -- Howdy end tell Now we're going to talk about some special features of the run handler of a script (not a script object). Variables defined implicitly in a script's top-level explicit run handler are implicit globals. In this respect, the explicit run handler is like the top level. For example: on run
set howdy to "Howdy"
sayHowdy( ) -- Howdy
end run
on sayHowdy( )
global howdy
display dialog howdy
end sayHowdy
But in other respects an explicit run handler is not like the top level. It's a handler, so you can't define a handler directly within it. And scopes outside an explicit run handler are not somehow magically transposed to be within it, as if it really were the top level. For example, this doesn't work: on run global howdy set howdy to "Howdy" sayHowdy( ) end run on sayHowdy( ) display dialog howdy -- error end sayHowdy Without its own global declaration for howdy, sayHowdy cannot see howdy any more; sayHowdy is not magically considered to be somehow inside the explicit run handler. A script's explicit run handler may take parameters; these must be expressed as a single list. This is useful only under very special circumstances, and prevents the script from running at all under normal circumstances! For example: on run {howdy} display dialog howdy end run That script is legal, but it won't run under normal circumstances because you have no way to pass the required single parameter to the run handler. But there are some contexts that give you a way to do it; for example, this is how you pass parameters to AppleScript code inside a REALbasic program. We'll see in Chapter 9 how you can pass parameters to a script's run handler. |
[ Team LiB ] |