[ Team LiB ] |
8.6 Handlers as ValuesA handler is a datatype in AppleScript. This means that a variable's value can be a handler. In fact, a handler definition is in effect the declaration (and definition) of such a variable. (That variable's status, as we have already seen, is essentially the same as that of a property.) The variable's name is the name of the handler, and its value is the handler's bytecode, its functionality. A handler may thus be referred to like any other variable, and you can get and set its value. For example: on sayHowdy( )
display dialog "Howdy"
end sayHowdy
set sayHello to sayHowdy
sayHello( ) -- Howdy
In that example, we stored a handler as the value of a variable, and then called the variable as if it were a handler! This works because the variable is a handler. The value of a handler can also be set. No law says you have to set it to another handler. For example, you could do this: on sayHowdy( )
display dialog "Howdy"
end sayHowdy
set sayHowdy to 9
display dialog sayHowdy -- 9
You can set one handler to the value of another, in effect substituting one entire functionality for another. Of course, the functionality has to be defined somewhere to begin with. For example: on sayHowdy( )
display dialog "Howdy"
end sayHowdy
on sayHello( )
display dialog "Hello"
end sayHello
set sayHello to sayHowdy
sayHello( ) -- Howdy
8.6.1 Handlers as ParametersAt this point, you're probably thinking: "Wow! If I can store a handler as a variable, I can pass it as a parameter to another handler!" However, this code fails with a runtime error: on sayHowdy( ) display dialog "Howdy" end sayHowdy on doThis(what) what( ) end doThis doThis(sayHowdy) -- error We did succeed in passing the handler sayHowdy as a parameter to doThis( ), but now we can't seem to call it; AppleScript refuses to identify the what( ) in the handler call with the what that arrived as a parameter. This is actually another case of the rule (Section 8.5.1, earlier in this chapter) that an unqualified handler call is a message directed to the current script object; AppleScript looks at the current script object, which is the script as a whole, for a global named what. One obvious workaround is to use such a global: on sayHowdy( )
display dialog "Howdy"
end sayHowdy
on doThis( )
global what
what( )
end doThis
set what to sayHowdy
doThis( ) -- Howdy
But globals are messy; we want a real solution. Here's one: we can pass instead of a handler, a script object. This is actually very efficient because script objects are passed by reference; and it works, because now we can use the run command instead of a handler call: script sayHowdy
display dialog "Howdy"
end script
on doThis(what)
run what
end doThis
doThis(sayHowdy) -- Howdy
Alternatively, we can define a handler in a script object dynamically and then pass it. This is more involved, but it permits both the script and the handler that receives it as a parameter to be completely general: script myScript
on doAnything( )
end doAnything
doAnything( )
end script
on doThis(what)
run what
end doThis
on sayHowdy( )
display dialog "Howdy"
end sayHowdy
set myScript's doAnything to sayHowdy
doThis(myScript) -- Howdy
Observe that we can actually redefine a handler within a script object, from the outside! My favorite solution is to pass a handler as parameter to doThis, just as in our first attempt, and to have a script object inside doThis waiting to receive it: on sayHowdy( )
display dialog "Howdy"
end sayHowdy
on doThis(what)
script whatToDo
property theHandler : what
theHandler( )
end script
run whatToDo
end doThis
doThis(sayHowdy) -- Howdy
This is much like the technique used in Section 8.5.1, earlier in this chapter. It also depends upon the remarkable rule (Section 7.4.2) that a script object within a handler can see that handler's local variables. Thanks to this rule, our property initialization for theHandler can see the incoming what parameter and store its value—the handler. Now we are able to call the handler using the name theHandler, because this is the name of a global (a property) within this same script object. For a useful application of this technique, let's return to the earlier example where we filtered a list to get only those members of the list that were numbers. The trouble with that routine is that it is not general; we'd like a routine to filter a list on any boolean criterion we care to provide. There are various ways to structure such a routine, but the approach that I consider the most elegant is to have filter( ) be a handler containing a script object, as in the preceding example. This handler accepts a list and a criterion handler and filters the list according to the criterion: on filter(L, crit) script filterer property criterion : crit on filter(L) if L = {} then return L if criterion(item 1 of L) then return {item 1 of L} & filter(rest of L) else return filter(rest of L) end if end filter end script return filterer's filter(L) end filter on isNumber(x) return ({class of x} is in {real, integer, number}) end isNumber filter({"hey", 1, "ho", 2, 3}, isNumber) I consider that example to be the height of the AppleScript programmer's art, so perhaps you'd like to pause a moment to admire it. 8.6.2 Handlers as Handler ResultsA handler may be returned as the result of a handler. Since you can't define a handler directly within a handler, you might have to define it as a handler within a script with the handler; but this is really no bother. So, for example: on makeHandler( ) script x on sayHowdy( ) display dialog "Howdy" end sayHowdy end script return x's sayHowdy end makeHandler set y to makeHandler( ) y( ) In and of itself, however, this is not tremendously useful; in real life, you're more likely to return the entire script object rather than just one handler from it. We'll see why in the next chapter. |
[ Team LiB ] |