I wrote in this space a few months back about Dave Winer's plan to put a system-level scripting language on the Mac. UserLand Frontier, which shipped in January, is the result of that plan: a programming environment for writing scripts that control the Macintosh operating system and/or applications. It includes a C-like scripting language called "UserTalk," and can be used to write scripts invocable from Frontier's own command-line sized window or to produce double-clickable files. Although Apple has long talked about something called AppleScript and has been talking about it more openly this spring, Frontier is the first and at this writing still the only system-level scripting software for the Mac. Having been developed more recently than the batch-processing languages of UNIX and MS-DOS, it is not surprising that Frontier is in many ways more powerful than these scripting predecessors.
Well, that's all good news for us Mac fanatics who have been waiting more or less patiently during the past eight years for some sort of system scripting language, but it isn't what I intend to discuss here. I do intend to discuss three programming concepts embodied in Frontier's scripting language, UserTalk--concepts that I think deserve consideration from anyone interested in the design of development environments. These are: outlining as a model for program structure, sharing the symbol table, and the concept of a documentation server.
There's no great novelty in noticing that program structure is largely hierarchical, nor in making that hierarchical structure visible by conventions of indentation, nor in supplying a program editor that knows about and enforces these conventions. What is novel about the use of outline structure in Frontier is the thoroughness with which it has been integrated into every aspect of the language, and the benefits that accrue from this no-compromise approach.
Dave Winer and Doug Baron, Frontier's creators, wrote the book on outlining on computers. Winer's entire career in software development has been an occasionally detoured pursuit of the program editor that works the way he thinks, and he thinks, apparently, in hierarchies. With his brother Peter and Doug Baron, he created and owned the application category of outline processors with ThinkTank, More, and Ready, until the concepts of outlining were encompassed by word processors and other programs.
The Winers and Baron invented those outlining concepts. Or at least they executed the first successful commercial implementations of them and established how outlining should look to the user. Here are the basic ideas:
Each line of an outline is called a "heading" or "subheading." Top-level headings are called "summits," and an outline can have more than one summit. All other headings are subheadings of some heading, and subheadings can have subheadings, in principle to any depth. Subheadings of a heading are indented by a standard amount.
A heading can be collapsed so that its subheadings disappear, and any heading with subheadings can be expanded to show them.
Navigation techniques such as special arrow-key definitions are provided to let the user move through the outline either as a hierarchical structure or as a flat block of text. Headings can be cut, copied, pasted, or moved, with or without their subheadings, by standard click-and-drag methods.
A capability called "hoisting" lets the user focus on one heading as though it and its subheadings were the whole universe. While collapsing hides headings at a lower level, hoisting hides headings at the same or a higher level.
Nobody has focused on these capabilities as closely as Dave Winer has over the past decade or so. So it is reasonable to assume that when Winer designed a language and put into it everything he had learned about outlining, there might be something to learn from looking at the result.
Outlining in Frontier serves the obvious purposes: to hide program detail and to reveal program structure. We can see how well it succeeds by looking at the way it handles the ubiquitous if and for structures.Example 1 shows a piece of Frontier code.
Other structured code is handled similarly. The case structure involves three levels of headings, with the case branches at the second level and the actions at the third. The on verb, used to define other verbs, has the same basic structure as for. (Frontier's vocabulary consists chiefly of functions, which the documentation calls "verbs.")
Mere indentation may reveal some structure, but it doesn't hide anything. An important feature of outlines is that all subheadings of a heading can be collapsed. In Frontier you collapse or expand the subheadings of a heading by double-clicking on the heading. So an if structure can be collapsed to the if statement itself or expanded to the full structure.
The fact that an if structure can be collapsed in this way suggests a useful commenting convention. Placing a comment on the same line as the if statement that summarizes the collapsed body of the if makes it possible to follow the logic of the program with the structure collapsed.
Although it's obvious, it may be worth pointing out that outlines break a certain symmetry of most programming languages: Blocks of code are not bracketed in Frontier by a structural statement at the beginning and end of the block. There are no end statements in Frontier. It may not be an important point, but I see it as desirable in eliminating a purely structural element from the language.
On the other hand, Frontier introduces its own purely structural element in the form of the bundle verb. Bundle is a device for grouping lines of code into logical blocks when they are not parts of any actual common structure. Example 2(a), for instance, isn't new with Frontier, but combined with the ability to collapse subheadings, it's a powerful tool for hiding detail and revealing structure. The initialization code in the example is an ideal candidate for bundling.
Another tool for structuring initialization code is the local declaration. The local verb has a conventional syntax: see Example 2(b). But you can also write that declaration like that in Example 2(c). This is a heading with subheadings, so it can be collapsed to look (with a comment added) like Example 2(dc).
Comments in Frontier don't have to be on the same line as code, of course. When a comment appears on a separate line, it is a heading, and all the outlining techniques apply to it. This suggests a commenting style in which the first comment of a block is a one-line summary, and the following, more detailed lines are subheads that can be collapsed or expanded. In fact, the Frontier editor will force any subheading of a comment to be a comment. This makes it easy to comment out a block of code, just by commenting its first line.
Every programming language has a symbol table.
What distinguishes Frontier's symbol table, called the "Object Database," is that it is permanent, browsable, editable, and hierarchical. But it's more than a symbol table, really, because it can hold various objects not necessarily having any connection with any code.
The fact that it is hierarchical is, of course, no surprise. A good deal of Frontier is outline structured. You can edit the program's (hierarchical) menus in an outline-processing window, create and save outlines using Frontier as an outline processor, and edit scripts as outlines. Although the Object Database isn't canonically presented as an outline, there is a facility for examining it in an outline window.
But the canonical view of the Object Database is as a hierarchy of tables, tables invariably consisting of one or more rows and the same three columns: name, value, and kind.
In the Object Database you can directly modify the name of any entry, the value of any scalar entry, and in some cases, the kind (that is, the data type). You can put pretty much any kind of data into the tables of the Object Database: the time of last backup, a to do list, a formatted form-letter template, the user name, a table of e-mail accounts, numbers in any standard numeric type, strings, chars, points, rectangle coordinates, RGB values for colors, or PICT-format pictures. The Object Database can also hold code; in fact, all scripts live in it, as does much of UserTalk.
Frontier puts global variables into the Object Database, and you can watch a variable change as a script runs, much as you would in a debugger window. Although to a user browsing through it the Object Database looks like a database, to a scripter it looks more like a symbol table or other internal data structure. From scripts, you address the objects in the Object Database by name, as in Example 3(a); by name and index, as in Example 3(b); or by address, as in Example 3(c)
In Example 4, the address operator @ can be undone by the dereference operator "^". This gives x the value of the Object Database address table.myStuff.
At least one table in the Object Database has a special purpose. The agents table holds scripts of agents. Agents are background scripts. They are written just like any other script, except that: 1. they must include the sleepfor verb that tells how long Frontier should wait before polling them, and 2. they reside in the agents table. Some sample agents shipped with Frontier are scripts that display the number of seconds since Frontier shipped, the current time, and the path to Frontier.
Agents actually have to be placed in the right table to work. In some cases, it is not obligatory to locate things in the right places in the Object Database, but it is smart.
You, as a Frontier third-party developer, would typically write not isolated scripts, but "suites" of scripts. A suite is a collection of scripts intended to be used together, along with appropriate menus, a means of invoking the whole suite, ditto for dismissing it, and documentation. A suite is a package of technology.
As a Frontier third-party developer, you should put your suites in the suites table of the Object Database. It's not obligatory, but when you realize that a Frontier user will have to move all the important stuff from one Object Database to another when installing a new version of Frontier, you'll see that keeping things in logical places is pretty important.
The Frontier documentation emphasizes the permanent and hierarchical nature of the Object Database, but I think that its openness is at least as significant.
Along with Frontier comes a stand-alone application called DocServer--a tool for displaying online documentation on Frontier verbs. All the basic and advanced verbs of the language are already documented in it as Frontier comes out of the box, but UserLand intends for developers to use it in documenting their own verbs and suites of verbs.
That's not too hard to do. DocServer reads from its own database, which it can update by importing data from a text file. So, you can create documentation either within Frontier or in a word processor and save it in a text file. Tabs separate the name of an item, such as "parameters," from the text of that item, and returns separate items. You can also add verb documentation to the database on-the-fly by sending Interapplication Communication (IAC) messages.
DocServer implements a simple, straightforward format for documenting commands. It's easy to follow the format and not so easy to violate it. DocServer doesn't give you much access to tab and font and other formatting decisions, but on the other hand it gives you great freedom in the chunks of information you choose to present. You can supply examples, a BNF-style definition, detailed information on required parameter, or whatever you think constitutes documentation for a Frontier verb, in any combination and quantity, and it'll all come out formatted pretty much the same. So there's not much reason not to use DocServer to document your verbs, and if you do, your documentation will have a familiar, standard look.
DocServer is linked to Frontier via IAC links, so when you control-click on a verb in any script, Frontier sends a request to DocServer to display the documentation for the verb. Hence the name DocServer. But this suggests a broader use for DocServer. Because it is IAC aware, any other application that can send messages ought to be able to drive DocServer, just like Frontier does. This is particularly significant, given what UserLand says about the licensing of DocServer:
DocServer is free. Like Apple's TeachText utility, DocServer should be on every script writer's machine, regardless of what scripting software he or she is using. So we will allow this product to be distributed at no cost with other developers' products. Contact UserLand Software for details.
As I read it, that means that DocServer can be used to document any kind of scripts on the Mac, including Quickeys macros, HyperTalk code, or AppleScript scripts, if and when there is an AppleScript.
DDJ
Copyright © 1992, Dr. Dobb's JournalCollapse the Clutter
Example 1: Typical Frontier code
for i = 1 to 12 << zero the month table
month[i] = 0
if fileIsFolder() << recurse through its files
getModDates(f)
else << get its modification date
modDate = fileModified(f)
Example 2: Tools for structuring initialization code: (a) using the bundle verb; (b) syntax of a local verb; (c) a UserTalk declaration; (d) collapsing a heading that has subheadings.
(a)
bundle << set path and menus
Frontier.pathstring = file.getpath()
menu.currentSuite = " "
(b)
local (cust = " ",amt = 0,custNo = 0)
(c)
local
cust = " "
amt = 0
custNo = 0
(d)
local << customer data
Share the Symbol Table
Example 3: Addressing objects in Frontier's Object Database, using (a) names; (b) names and indexes; (c) addresses.
(a)
table.myStuff
(b)
table [1]
(c)
@table.myStuff
adr = @table.myStuff
x = addr^
Example 4. Using @ to undo the address operator.
Using @ to undo the address operator
addr = @table.myStuff
x = addr^
Free the Documentation