DekGenius.com
[ Team LiB ] Previous Section Next Section

8.6 Handlers as Values

A 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 Parameters

At 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 Results

A 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 ] Previous Section Next Section