According to Script: Think About Dictionaries

Cal Simone

I've been thinking lately about the purpose of this column, which debuted in the
previous issue of develop. Permit me to take a moment to say something about that
before I get down to some tips about dictionaries.

During the first couple of years after the birth of the Macintosh, there was a period of
chaos, when application developers were figuring out how to extend the basic user
interface. For example, some of the most commonly used menu commands appeared in
different locations in various applications, and, more important, keyboard shortcuts
varied or sometimes weren't present at all. After a while, though, things settled down
and almost everyone adopted the standards that were eventually documented in the
Macintosh Human Interface Guidelines.

AppleScript is the alternate user interface to your application. Now that AppleScript
has been available for two years, it's time to move out of the "free-for-all" and
develop the same consistency we've all come to enjoy and expect from the Macintosh
experience. That's what this column (and the work I do in the AppleScript development
community) is all about -- encouraging consistency. The tips I offer here reflect
undocumented conventions followed by many developers I've worked with, as well as
my own thinking about scriptability. Until the time when standards are documented in
a "Macintosh Human Scriptability Guidelines," I encourage you to adopt the techniques
suggested here.

Though I've said it before, I'll say it one more time: adopting the object model is the
single most important factor contributing to consistency in the AppleScript language
across applications of different types. One developer I know resists using the object
model year after year, arguing that it "isn't appropriate for everything." But the fact
is that the object model  has been successfully applied to a whole range of applications.
Every major C++ framework now supports it or has add-ons to support it, and
up-and-coming languages will support it. Even if your application has only one object
(such as the dictionary of a small paging program I've seen), just do it!

ORGANIZING YOUR DICTIONARY

So far in the scripting world, various developers have used different schemes in their
dictionaries for organizing the events in a suite, the parameters in an event, the
properties in an object, and so forth. Some organize them according to their function,
others  order them alphabetically, and still others don't seem  to have any scheme
whatsoever (probably because scripting support was added a bit at a time or as an
afterthought). For the sake of consistency across different scriptable applications,
using some standard scheme is preferable.

  If you're including an entire standard suite (such as  the Core suite) from
AppleScript's system dictionary (listed in the Rez files named EnglishTerminology.r,
FrenchTerminology.r, and so on) and then overriding or extending the suite to add your
own terms, make sure that your overrides appear in the same order as they do in the
system dictionary and that extensions come after all the overrides. If you're
implementing your own terminology, either as extensions to existing suites or in your
own suites, organize it as described in the following paragraphs.

When you're adding new terms to a previously created dictionary (for example, when
upgrading your application to provide deeper scripting support), remember to insert
the new terms according to the same scheme or schemes you originally implemented.
It's a good idea to keep some notes in your internal design documents describing the
ordering schemes  you used, so that you can be consistent with your earlier work
(unless you're redoing your scripting implementation from scratch -- for instance,
when you're converting from an old non-object model implementation to the object
model).

Suites. So that your dictionary is consistent with dictionaries in other applications,
include the standard Registry suites first (Required suite first, then the Core suite,
then any other Registry suites). Then include any custom suites you create.

Events. Order commands that correspond to events in one of four ways: by likelihood
of use, according to function, chronologically, or alphabetically. The method you choose
will depend on how your application is used and the nature of your users. As an
example of each of these schemes, I'll show how some of the Core suite verbs might be
organized.

If certain commands are to be used more frequently than others, order them according
to likelihood of use. Present those commands that will be used most frequently at the
beginning and those seldom used at the end:

get(more of these than anything else)
set(quite a few of these, too)
count(a fair amount of counting)
make(sometimes new objects are created)
open(sometimes they're opened)
close(and closed)
print(printing isn't done as frequently)
delete(neither is deleting)
quit(quitting is done only occasionally)

If your users will logically group the operations, use an ordering according to
function. Group together commands that are related in some way:

make(make and delete)
delete
open(open and close)
close
set
(set and get)get
count(the rest are unrelated)
print
quit

If the commands are normally used in a certain order, choose a chronological ordering.
First present the commands that will be used first, followed by the commands that will
be used later:

make(this often comes first)
open(or else opening comes first)
set(then setting properties)
get(and later getting properties)
count(counting comes in the middle)
print(printing happens later)
close(then comes closing)
delete(deleting is near the end)
quit(last, we bail out)

If the commands aren't going to be used in any particular order, or you don't know
what that order is likely to be, and there's no logical grouping, list the commands
alphabetically, as the Core suite does. Although alphabetical order isn't as helpful as
the other schemes, script writers will at least be able to find commands more easily in
your application's dictionary.

Parameters. Make an effort to list parameters in  an order that encourages the
writing of natural, grammatically correct sentences for commands. For example:

make   new  <TYPE CLASS>
      [at  <LOCATION REFERENCE>]
      [with data  <ANYTHING>]
      [with properties  <RECORD>]

 

If the order of an event's parameters doesn't matter as far as sentence style is
concerned, order them according to the frequency of likely use.

close  <REFERENCE>
      saving  <YES ="no|ask">
      saving in  <FILE SPECIFICATION>

 

Object classes and properties. I'd suggest placing the outermost objects in your
containment hierarchy first, objects contained in the outermost objects next, and
objects that don't contain any other objects last. Remember that every object class
representing an actual object must be listed as an element of some other object,
eventually leading back to the application class (the null container). Primitive class
definitions and record definitions (which aren't part of the containment hierarchy)
and abstract classes (which aren't instantiable objects but are used to hold lists of
inherited properties) should be placed in the Type Definitions or Type Names suite,
and clearly labeled as a record definition or abstract class. (See my article, "Designing
a Scripting Implementation," in develop Issue 21.)

Properties of objects can be ordered according to one of the schemes described above
for events.

WHEN YOU ALLOW MULTIPLE VALUE TYPES

Occasionally in your dictionary you might need to specify a parameter or property for
which any of several types is acceptable. Using the wild card ('****') as the type of a
parameter or property tells your user that you'll accept anything (or at least a wide
variety of mixed types). Don't do this to be lazy or to finish your dictionary quickly;
do it only if you mean it. If you accept only one type, explicitly indicate so. If you allow
two different types, you can either create a compound "type" or use identical keyword
entries.

Defining a compound "type." One way of handling cases where you can accept two
different value types for a parameter or property is to make up a new "type" to
represent a combination of acceptable types in your dictionary. This isn't a real type
that you'd have to check for or deal with in your application's code, but instead just
serves to indicate in your dictionary that your application will handle either type.
This works particularly well when the value types are simple. For example:

class reference or string: Either a reference or
   a name can be used.

 

You can use your new "type" in a parameter or property definition as follows:

class connection
properties:
   window  <REFERENCE OR STRING>  -- the
      connection's window can be referred to
      either by a reference or by its name

 

To define a new type, make a new object class and place it in the Type Names suite (see
my article in Issue 21).

Using identical keyword entries. You can also use multiple entries with
identical keywords to specify alternative ways of filling in a parameter or property
value. This works well when the value types are complex or are highly dissimilar. For
example, the display dialog command has two with icon listings, one for specifying the
icon by its resource name or ID and the other for displaying the stop, note, or caution
icon:

display dialog  <ANYTHING>  -- title of dialog
   ...  other parameters
   [with icon  &lg;ANYTHING>]  -- name or id of the
      icon to display
   [with icon  <STOP ="note|caution">]  -- or display
      one of these system icons

 

Note the use of "or" in the second entry's comment: make sure you use the same 4-byte
ID for both parameter entries.

Although you could have many entries to show every possible individual type that a
parameter or property takes, this might become confusing to the user. So I'd
recommend that you use this sparingly, and when you do use it, try to limit the
number of similar entries to 2.

MAKING USE OF THE COMMENT AREA

You can use the comment area (available for each suite, event, parameter, class, and
property entry) to help clarify how your vocabulary is to be used. Since your
dictionary is often the initial "window" through which a user looks to figure out what
to do, descriptive comments can make the user's task a lot easier. And remember that
your users aren't necessarily programmers, so you should avoid terms like FSSpec  in
your comments. I'll give some examples to show  you what I mean.

Keep in mind that if you include an entire standard suite (such as the Core or Text
suite), your own comments should reflect the style of the comments in that suite. See
the Scriptable Text Editor's dictionary as an example of fairly good comment style; it
shows the standard versions of the Required, Core, and Text suites and adds some of its
own terminology.

A COUPLE MORE DICTIONARY TIPS

While I'm on the subject of dictionaries, here are a couple of extra tidbits. Use only
letters and numbers for terms in dictionaries. Don't use a hyphen (-), a slash (/), or
any other nonalphanumeric characters in your dictionary entries. For example, if you
use Swiss-German, AppleScript will treat it as Swiss - German (subtraction), which
is not what you want; if you use Read/write, it will be treated as Read / write
(division). Note that Read/write is in the standard Table suite, but it won't compile
properly.

All terms must start with letters. Using 9600 as an enumerator won't work; you
would have to use something like baud9600.

Finally, pick names for your terms that are descriptive for a user, especially a
nonprogrammer. If you pick a term like x, users won't be allowed to use x as a
variable name in their scripts. For instance, instead of "x <small integer> -- the x
coordinate" use "horizontal coordinate <small integer> -- the x coordinate."

IT'S YOUR THING

Unlike writing code, designing a scripting vocabulary isn't an exact science. It's up to
you to decide in what manner (and how effectively) humans will interact with this new
interface. Applying "programming language" concepts and standards won't always
work. You need to keep an eye toward the human aspects of the AppleScript language
and to work out a scheme that reflects careful attention to your users.

  You may occasionally see guidelines here that aren't completely clear-cut or that
even conflict with each other, and every so often I'll adjust what I've said in  an earlier
column. This is the nature of an evolving language. If you're not completely at home
with this, seek out an expert in scriptability design for advice.  But remember,
vocabulary design is by nature as much art as science.

CAL SIMONE (AppleLink MAIN.EVENT) works way too hard at Main Event Software in
Washington DC. He took his last summer vacation five years ago; it's been so long, he's
forgotten what a vacation is like, and he can't imagine where he'd go. He's been  to
beautiful mountainous places like Colorado, Alaska, British Columbia, and
Switzerland, and to a few islands like Saint Thomas and the Bahamas. Cal would really
like to hear your suggestions on possible future topics for this column, as well as your
ideas for good vacation spots.*

Thanks to Sue Dumont and C. K. Haun for reviewing this column.*