DekGenius.com
[ Team LiB ] Previous Section Next Section

9.7 Inheritance

Script objects may be linked into a chain of inheritance. If one script object inherits from another, the second is said to be the parent of the first. If a message is sent to a script object and it doesn't know how to obey it, the message is passed along to its parent to see whether it can obey it. A message here is simply an attempt to access any top-level entity.

To link two script objects explicitly into a chain of inheritance, initialize the parent property of one to point to the other.

The parent property may be set only through initialization. You cannot use copy or set to set it.


In this example, we explicitly arrange two script objects, mommy and baby, into an inheritance chain (by initializing baby's parent property). We can then tell baby to execute a handler that it doesn't have, but which mommy does have. Here we go:

script mommy
        on talk(  )
                display dialog "How do you do?"
        end talk
end script
script baby
        property parent : mommy
end script
baby's talk(  ) -- How do you do?

In that example, we told the child from outside to execute a handler that it doesn't have but the parent does. The child can also tell itself to execute such a handler:

 script mommy
        on talk(  )
                display dialog "How do you do?"
        end talk
end script
script baby
        property parent : mommy
        talk(  )
end script
run baby -- How do you do?

Getting and setting properties works the same way. In this example, we get and set the value of a property of baby that baby doesn't have:

script mommy
        property address : "123 Main Street"
end script
script baby
        property parent : mommy
end script
display dialog baby's address -- 123 Main Street
set baby's address to "234 Chestnut Street"
display dialog mommy's address -- 234 Chestnut Street

Again, the same thing can be done from code within the child; but now the name of the property must be prefixed with the keyword my. Otherwise, since there is no property declaration in scope for this name, the name is assumed to be the name of a local variable. The keyword my says: "This is a top-level entity of the script object running this code." Thus, if AppleScript fails to find such a top-level entity in the script object itself, it looks in the script object's parent.

script mommy
        property address : "123 Main Street"
end script
script baby
        property parent : mommy
        on tellAddress(  )
                display dialog my address
        end tellAddress
end script
baby's tellAddress(  ) -- 123 Main Street

Similarly, we can refer to a script object that the child doesn't have but the parent does:

script mommy
        script talk
                display dialog "How do you do?"
        end script
end script
script baby
        property parent : mommy
end script
run baby's talk -- How do you do?

Again, if the child wants to do this, it must use my:

script mommy
        script talk
                display dialog "How do you do?"
        end script
end script
script baby
        property parent : mommy
        run my talk
end script
run baby -- How do you do?

9.7.1 Polymorphism

When code refers to a top-level entity, the search for this top-level entity starts in the script object to which the message that caused this code to run was originally sent. This is called polymorphism. You may have to use the keyword my to get polymorphism to operate (and it's probably a good idea to use it in any case).

An example will clarify:

script mommy
        on tellWeight(  )
                display dialog my weight
        end tellWeight
end script
script baby
        property parent : mommy
        property weight : "9 pounds"
end script
baby's tellWeight(  ) -- 9 pounds

We ask baby to tell us its weight, but baby doesn't know how to do this, so the message is passed along to the parent, mommy. There is now an attempt to access my weight. But mommy has no top-level entity called weight. However, the search for weight starts with baby, because our original message was to baby (mommy is involved only because of inheritance). The property is found and the code works.

To see why my is important here, consider this code:

script mommy
        property weight : "120 pounds"
        on tellWeight(  )
                display dialog weight
        end tellWeight
end script
script baby
        property parent : mommy
        property weight : "9 pounds"
end script
baby's tellWeight(  ) -- 120 pounds

There is no my before the name weight, and mommy's declaration for the property weight is in scope, so the name is simply identified with this property; polymorphism never has a chance to operate.

The reason for the "poly" in the name "polymorphism" is that the response to the parent's use of a term can take many different forms. A parent whose code is running because of inheritance has no idea of this fact, so it has no idea what its own code will do. For example:

script mommy
        property weight : "120 pounds"
        on tellWeight(  )
                display dialog my weight
        end tellWeight
end script
script baby
        property parent : mommy
        property weight : "9 pounds"
end script
script baby2
        property parent : mommy
        property weight : "8 pounds"
end script
mommy's tellWeight(  ) -- 120 pounds
baby's tellWeight(  ) -- 9 pounds
baby2's tellWeight(  ) -- 8 pounds

In that example, the parental phrase my weight gets three different interpretations, depending solely on what script object was addressed originally.

9.7.2 Continue

A child can call an inherited handler by using the continue command. The syntax is the keyword continue followed by a complete handler call (parameters and all).

You might wonder why this is needed, since after all the child can just send a message directly to the parent by referring to the parent as parent. But there's a crucial difference. If a message is sent to the parent by referring to it as parent, that's a new message with a new target. On the other hand, the continue command takes place in the context of the current message and the current target; it passes the current flow of control up the inheritance chain. Thus, the one breaks polymorphism, the other does not.

This example demonstrates the difference:

script mommy
        property weight : "120 pounds"
        on tellWeight(  )
                display dialog my weight
        end tellWeight
end script
script baby
        property parent : mommy
        property weight : "9 pounds"
        parent's tellWeight(  )
        continue tellWeight(  )
end script
run baby -- 120 pounds, 9 pounds

9.7.3 The Implicit Parent Chain

A script object without an explicitly specified parent has as its parent the script as a whole.

We took advantage of this fact earlier (see the end of Section 7.5.1) to refer to a top-level script property. Thus:

property x : 5
script myScript
        property x : 10
        display dialog my parent's x
end script
run myScript -- 5

But there's a parent beyond that. The script as a whole has as its parent the AppleScript scripting component. This appears to your code as a script object called AppleScript.

The AppleScript script object has some properties that you can access. Normally you do this without having to refer to AppleScript explicitly, because these properties are globally in scope; it's as if every script were surrounded by another invisible script with property declarations for these properties. But in a context where a name overshadows the name of one of these properties, it would be necessary to be explicit, in order to jump past the current scope and up to the level of AppleScript:

set pi to 3
display dialog pi -- 3
display dialog AppleScript's pi -- 3.141592...
display dialog parent's pi -- 3.141592...

The AppleScript script object is also where the built-in verbs live. For example, when you say:

get 3 + 4

the get command travels up the inheritance chain until it reaches the AppleScript scripting component, which knows how to obey it.

The AppleScript scripting component has a parent too—the current application. This is the host application that summoned the AppleScript scripting component to begin with. The current application is the absolute top level, and can be referred to in code as current application. For example:

display dialog (get name of current application) -- Script Editor

To sum up:

script myScript
        my parent -- «script», the anonymous top level
        my parent's parent -- «script AppleScript»
        my parent's parent's parent -- current application
end script
run myScript

Observe that scope-wise containment is not implicit parenthood:

script myScript
        script myInnerScript
                my parent -- the anonymous top-level script, not myScript
        end script
        run myInnerScript
end script
run myScript

Nor can a contained script object be made to have a containing script object as its parent. AppleScript will balk if you try this:

script myScript
        script myInnerScript
                property parent: myScript -- compile-time error
        end script
end script

I think the reason for this restriction must be that the demands of parenthood would conflict irresolvably with the rules of scoping.

    [ Team LiB ] Previous Section Next Section