Online Book Reader

Home Category

AppleScript_ The Definitive Guide - Matt Neuburg [87]

By Root 1387 0

on doThis(what)

what( )

end doThis

doThis(sayHowdy) -- error: «script» doesn't understand the what message

The trouble is that 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 (see "Handler Calls, Commands, and Script Objects" in Chapter 8) that an unqualified handler call is a message directed to the current script object, which in this case is the script as a whole.

One possible workaround is to use a global. This approach works because by copying the handler to a global we're putting it where a message directed to the script as a whole can find it (see "Scope of Globals" in Chapter 10):

on sayHowdy( )

display dialog "Howdy"

end sayHowdy

on doThis(what)

global what2

set what2 to what

what2( )

end doThis

doThis(sayHowdy) -- Howdy

This solution is clever, but now we've broken encapsulation. Global variables pose risks (other code might access this same global, or we might be tromping accidentally on some other code's global), and besides, if we're going to use a global there's little point to passing a parameter in the first place.

Another possible workaround is to pass a script object instead of a handler:

script sayHowdy

display dialog "Howdy"

end script

on doThis(what)

run what

end doThis

doThis(sayHowdy) -- Howdy

This is very efficient because script objects are passed by reference. But we ended up having to use the run command instead of a handler call. That's not going to be very pretty if our handler takes any parameters, because it's hard to pass parameters to a run handler (as shown earlier in this chapter). But wait—a script object can contain a handler! So we can use it as a kind of envelope. We can define a handler in a script object and pass the script object. In fact, this device 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

However, there's another way entirely. This happens to be my favorite solution. We don't pass a script object; we pass a handler, just as in our first attempt. But inside the handler, we have a script object 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

The fact that this code works is astonishing (at least, when I stumbled upon it I was astonished). It depends upon an obscure but powerful rule of scope (see "Scope of Locals" in Chapter 10) which says that a script object within a handler can see that handler's local variables. An incoming parameter is such a variable. Thanks to this rule, our property declaration for theHandler can see the incoming what parameter and store its value. So now the property theHandler is a handler! But it is also a top-level entity of the script object, which means that we can call it from within this same script object. Tricky, eh?

For a useful application of this technique, let's return to the example earlier in this chapter where a handler called filter filtered a list to get only those members of the list that were numbers. That handler is not general; we'd like a way to filter a list on any criterion we care to provide. So we want to pass it both a list and a handler (a handler that takes a single argument and returns a boolean saying whether it fits the criterion). We can do so by writing filter( ) as a handler containing a script object:

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,

Return Main Page Previous Page Next Page

®Online Book Reader