Previous Page
Next Page

20.2. Terminology Clash

Things don't always go smoothly when AppleScript resolves terminology. Terms are sought in the dictionaries of the innermost application, of AppleScript itself, and of all scripting additions, as well as in the script. Given such a large namespace comprising contributions from multiple independent entities, it is possible for conflicts to arise. Such a conflict is called a terminology clash . Either the programmer generates the clash by an unwise choice of variable names, or different dictionaries generate it by defining the same term in different ways.

When the programmer causes a terminology clash, various things can happen. Sometimes the code won't compile; sometimes it won't run; sometimes it runs but gets an unexpected result; sometimes the clash is resolved sensibly and there's no problem.

20.2.1. Compile-time Error

When the compiler stops you from using a term, the term is probably defined elsewhere as a different "part of speech" from how you're trying to use it.

For example, this won't compile:

local count
-- compile-time error: Expected variable name or property but found command name

The term count is defined by AppleScript itself as a command. Thus you're effectively trying to use a verb where a noun is expected.

This won't compile:

set sel to {length:2, offset:4}
-- compile-time error: Expected variable name, class name or property but
found command name

This is similar to the previous example: offset is defined as a command in a scripting addition. Observe that length doesn't cause a clash here, even though it's defined in AppleScript's own dictionary; that's because it's defined as a property, and you're using it as a property (see "Record Properties" in Chapter 13).

This won't compile:

local desktop
-- compile-time error:
-- Expected variable name or property but found application constant or consideration

Again, the problem is a scripting addition; desktop is a constant, part of an enumeration used in the path to command. (See "Enumerations," later in this chapter.)

When you don't declare a variable, the error message is different, though the cause is the same:

set desktop to 7 -- compile-time error: Can't set desktop to 7. Access not allowed

A constant isn't something that can be assigned to.

In those examples, the clash was with a term defined by AppleScript or a scripting addition. Within a tell block, the same sort of thing can happen with respect to a term defined by the target application:

tell application "Finder"
    set container to 7 -- compile-time error: Can't set «class ctnr» to 7.
    Access not allowed
end tell

Within the context of a tell block targeting the Finder, container is resolved as the Finder's term container, which is a class name. A class can't be assigned to.

Here's an example where the error message is particularly unhelpful:

tell application "Finder"
    script eject -- compile-time error: Expected "tell", etc. but found "script"
    end script
end tell

The cause is that eject is a command defined by the Finder.

One more example. One day I was confused when the filter example at the end of "Handler and Script Object as Parameter" in Chapter 9 refused to compile. The troublesome line was apparently this one:

    return filterer's filter(L)
    -- compile-time error: Expected expression but found command name

It turned out that a recently installed scripting addition (the Satimage osax) contained a filter command.

20.2.2. Runtime Error

As we've just seen, you can't use the name of a command as a declared variable. If you don't declare it, though, you can get away with using the name of a command as a variable at compile timeonly to be caught out at runtime, with a very strange and ungrammatical form of error:

set count to 0 -- error: Can't set count of 0 to

It's okay to use a defined property name as the name of a variable, but not if you don't declare it:

set year to 2005 -- error: Can't make year into type reference

If you do declare it, it's fine:

local year
set year to 2005 -- fine

20.2.3. No Error, Surprising Behavior

A terminology clash where the compiler doesn't complain and there is no runtime error is potentially the worst case for the programmer, because the code runs but it doesn't behave as expected. Here's an example:

local container, x
set container to "howdy"
tell application "Finder"
    set x to container
end tell
x -- container, not "howdy"

The programmer was trying to set the variable x to the value of the variable container. But in the context of a tell block targeting the Finder, container is the name of a class, so the variable x ends up with that class as its value.

Here's a similar example from "Me" in Chapter 11:

set home to "Ojai"
tell application "Finder"
    get home
    -- folder "mattneub" of folder "Users" of startup disk of application "Finder"
end tell

If you create a handler name that clashes with the name of a scripting addition command, you can make it impossible to call the scripting addition command. For example, the beep scripting addition command is supposed to take one optional parameter, the number of times to beep. If you define a handler called beep, the beep scripting addition command becomes inaccessible:

on beep (what)
    display dialog what
end beep
beep 3 -- 3, not beep beep beep

AppleScript accepts the syntax beep 3 because that's correct syntax for the event as defined in the scripting addition; but at runtime it routes the call to your handler.

20.2.4. Detecting Terminology Clash

Terminology clash that prevents compilation is good; the script won't compile, but at least you were saved from the problem before execution of the script was underway. Terminology clash that doesn't prevent compilation is a tougher nut to crack: either there's going to be a runtime error or, even worse, the script will execute without error but will do the wrong thing. How can this situation be avoided?

An important clue that a terminology clash is brewing is the pretty-printing of the compiled and decompiled script in your script editor application. (The pretty-printing occurs after compilation but before execution, so this is a good reason to compile and run in two separate steps.) AppleScript's preferences can be set so that, when your script is pretty-printed, terms defined in your script have a different color or style from other terms. (To set these preferences, use the Preferences dialog of a script editor application.) If you think a term is being defined in your script, but it doesn't have correct color and style, trouble may lie ahead. For example:

set count to 0
set myVar to 10

On my machine, count and myVar are colored differently. This is a bad sign, and should make me stop and think before executing this script. Why don't they have the same status? Aren't they both variable names defined by my script? Thinking about this, I might realize that count is the name of a command defined by AppleScript, and is a bad choice of name for a variable.

Look back over earlier examples. In the example with home, the local variable home and the home in the tell block targeting the Finder appear in different colors in my script editor application; this should tell me they refer to two different things, which is not my intention, so the script is going to misbehave. Exactly the same thing happens in the example with container. In the example with beep, the term beep is the wrong color everywhere; this should warn me that I may be trying to use a term that's already defined elsewhere, which may have unexpected results.

20.2.5. No Terminology Clash

Sometimes the pretty-printed terminology colors seem to threaten a terminology clash, but there isn't one. For instance:

local year
set year to 2005 -- fine

The term year is the wrong color, but the script runs fine. We may conclude that an explicitly declared variable whose name is that of a defined property is not problematic. The reason, apparently, is that a variable name and a property name are very much alike, and can be used in similar ways. If a name is invented by the programmer, it is one color; if it is already defined as a property name, it's another color (and behind the scenes its four-letter code is used). But syntactically they are largely interchangeable, as long as AppleScript doesn't get confused about what role the name is playing in its current context. That, presumably, is why the declaration is necessary: it says to AppleScript, "I know this is an existing property name, but I'm using it as the name of a variable here." (Recall how the name of a record item can be an existing property name or a "user property" namesee "Record Properties" in Chapter 13.) As a result, and rather surprisingly, this code works perfectly:

local bounds
tell application "Finder"
    set bounds to (get bounds of item 1)
end tell
bounds -- {-33, -33, 31, 31}

Within the tell block, the term bounds is being used in two different ways, yet AppleScript knows which is which. The reason, ironically, is that bounds is already defined by AppleScript as the name of a property. Thus all four occurrences of bounds are the same color when pretty-printed: they are all property names. Just as important, they have the same four-letter code: they are all the same property name. The variable declaration at the start puts bounds in scope as a variable name; inside the tell block, it can also be resolved as the name of a Finder property. In the third line, AppleScript identifies the second bounds (the one after get) with the Finder's bounds, but it identifies the first bounds (the one after set) with the variable bounds defined in the first line. The difference is that the first bounds (the one after set) has no of (or its, which would amount to the same thing); thus it is not explicitly said to be a property of the Finder, and is identified with the variable bounds instead.

To understand better the circumstances under which this sort of thing works, contrast some situations in which it doesn't work. It doesn't work if there is no explicit declaration of the variable bounds:

tell application "Finder"
    set bounds to (get bounds of item 1)
    -- error: Finder got an error: Can't set bounds to {-33, -33, 31, 31}
end tell

In that example, the first bounds is identified with the Finder's bounds property, because there's nothing else for it to be identified with.

And here, the variable name home, though declared explicitly, cannot be identified with the home in the tell block, because the former is a name created by the user; it is not an existing property name whose four-letter code matches that of the Finder's home:

local home
tell application "Finder"
    set home to (get bounds of item 1)
    -- error: Finder got an error: Can't set home to {-33, -33, 31, 31}
end tell

20.2.6. Resolving Terminology Clash

If you are aware of a terminology clash caused by your choice of name, your best option is to avoid the conflict altogether: don't use problematic names in the first place! If you insist upon using a problematic name, however, you can usually resolve the conflict (as explained earlier in the section "Me" in Chapter 11) by using pipes (vertical bars) to suppress AppleScript's interpretation of something as a dictionary term. Many of the examples that wouldn't compile or run correctly earlier in this section work perfectly if you add pipes:

local |count|
set |count| to 0
set |year| to 2005
set sel to {length:2, |offset|:4}
local |desktop|
tell application "Finder"
    set |container| to 7
end tell
tell application "Finder"
    script |eject|
    end script
end tell
on |beep|(what)
    display dialog what
end |beep|
beep 1 -- beeps
|beep|("howdy") -- howdy

If a scripting addition was not present when a script was compiled and is present when the script is decompiled, pipes can appear spontaneously around terms that would cause a conflict with the scripting addition. In effect, AppleScript has inserted the pipes to resolve the terminology clash! That's a useful mechanism, but it can surprise the user. So, for example, if the filter example at the end of "Handler and Script Object as Parameter" in Chapter 9 is compiled and saved, and then you install the Satimage osax and open the compiled script, all instances of filter will have been changed to |filter|.

Bear in mind that there may be more at stake than terminology clash. There is also the problem of who the target is. Recall this earlier example:

set home to "Ojai"
tell application "Finder"
    get home
    -- folder "mattneub" of folder "Users" of startup disk of application "Finder"
end tell

The programmer wanted to refer from inside the tell block to the home in the first line. The problem is that there is a terminology clash and the second home is being directed to the Finder instead of the script. It isn't enough retarget the second home:

set home to "Ojai"
tell application "Finder"
    get my home -- error: Can't make home directory into type reference
end tell

That's because there's still a terminology clash. The correct solution is to resolve the terminology clash:

set home to "Ojai"
tell application "Finder"
    get |home| -- "Ojai"
end tell

This example from Chapter 11, on the other hand, is not a terminology clash:

on reverseString(s)
    set text item delimiters to ""
    return (reverse of characters of s) as string
end reverseString
tell application "Finder"
    set name of folder 1 to reverseString(get name of folder 1)
    -- error: Finder got an error: Can't continue reverseString
end tell

The color of the term reverseString in the tell block shows that, to the compiler, this is the same reverseString defined as a handler earlier. But that doesn't matter; the handler call is still going to be sent as a message to the Finder, not to the script. Adding my solves the problem. (See "Handler Calls, Commands, and Script Objects" in Chapter 8, as well as "Terms in Scope" and "Me" in Chapter 11. If your choice of names is sufficiently perverse, you may have to use both my and pipes, as in the reveal example in Chapter 11.)

20.2.7. Clash Between Dictionaries

If a clash is caused by the programmer, the programmer can choose different terms and avoid the clash. But what happens when the same term is defined in two different dictionaries, which are not in the programmer's power?

Clashes between the dictionaries of scriptable applications are not problematic, as you can target only one scriptable application at a time. For instance, both Entourage and the Finder define the term folder, but unless you deliberately pervert the terminology-resolution mechanism with a terms block, no conflict will ever arise.

A term defined in both AppleScript's and an application's dictionary is not necessarily problematic either. In fact, it's expected, provided they are the very same termthe same English-like term and the same underlying four-letter code should be used (and, I should probably add, both dictionaries should use the term as the same part of speech). For example, AppleScript defines the term name ('pnam') as a property of a script object; the Finder defines it as a property of an item. As they both use the same English-like term and the same four-letter code, and they both use it as a property, this is not a conflict. Similarly, both AppleScript and the Finder implement the count command.

Nevertheless, a dictionary can be badly written. The authors of a dictionary are supposed to do their homework, making sure they don't use any terminology that might conflict with AppleScript itself. Sometimes, they don't do their homework (see "Clashes with AppleScript," later in this chapter). Even more insidious is what happens when a scripting addition's dictionary gets into the act. After all, even if an application's dictionary generates a terminology clash, at least the problem arises only if you're targeting that application. But when a scripting addition makes the same kind of mistake, it conflicts everywhere, and there's nothing you can do about it (short of not using that scripting addition). You can't target a scripting addition explicitly, and you can't hide the scripting addition's terminology from your script. And an application developer, creating an application's dictionary, even by doing all the homework in the world, can't guess what third-party scripting additions you might install and what conflicts these might cause. Thus the real trouble lies in the nature of the scripting addition mechanism itself, which invites such clashes; this is one of the reasons why Apple discourages developers from writing scripting additions (Chapter 21).

Not surprisingly, there have historically been many instances of clashes caused by scripting additions. Thoughtful scripting addition developers give their terms deliberately improbable names to reduce the likelihood of clashes. Sometimes a user notices a clash and notifies the scripting addition's developer, who responds by creating a new version of the scripting addition with an altered dictionary. Nonetheless, there are probably still some scripting additions where terminology clash is simply part of the price of using them.

Here's a mysterious terminology clash that isn't solved by pipes:

local folder
set folder to 5
tell application "Finder"
    set |folder| to 10
end tell
folder -- 5, not 10

The trouble is that folder is also defined in a scripting addition, as the name of a property. (It's a property of the file information record returned by the info for command. The scripting addition defines folder as 'asdr', but the Finder defines it as 'cfol'.) To use folder as a variable name successfully, if we are going to put pipes around it anywhere, we must put pipes around it everywhere, so that it isn't identified with this scripting addition property:

local |folder|
set |folder| to 5
tell application "Finder"
    set |folder| to 10
end tell
|folder| -- 10

An infamous example for many years was this:

tell application "BBEdit"
    tell text window 1
        offset of "i" in (get contents of word 1)
        -- compile-time error: access not allowed
    end tell
end tell

The trouble was caused by a conflict between BBEdit's offset property and the offset scripting addition command. Consequently there was no straightforward way to use this scripting addition command while targeting BBEdit. Fortunately, recent changes in BBEdit's dictionary have effectively resolved the problem.


Previous Page
Next Page