I'd like to describe the special-purpose language that I have designed for my interactive storytelling engine. Now, there are a number of constraints that I just can't get around; for example, the core concept of the interactive storytelling technology relies heavily on mathematical concepts. Therefore, the custom language uses mathematics. However, I have gone to great lengths to take care of all the housekeeping details that normally bedevil programmers. Some of the tricks I have thought up will illustrate what I mean by a programming language that uses more of the computer's resource to make life easier for the user.
The first design concept that I settled upon was simple: there will be no syntax errors in my language. A syntax error in programming almost always arises from some petty typographical mistake: a misspelled command, an absent parenthesis or semicolon, perhaps two numbers in the wrong order. Syntax errors are seldom a major headache but they do slow you down. More important, the very concept of the syntax error reverses the proper relationship between human and computer. The syntax error presumes that the human has typed up his program, and is now submitting the program for the approval of the compiler, which rejects the human's submission as flawed. Good God, how utterly arrogant! Why should a human being stand in the role of supplicant to a computer program? That's utterly backwards! If the computer is smart enough to know what's correct and incorrect, it should be smart enough to help the human avoid that kind of mistake in the first place.
My method for preventing syntax errors is simple: I prevent typing. You don't type a program in my language; you pick and choose the parts that go into it. A program in my language isn't typed in, it's assembled like tinkertoys. The various parts that make up the language are spread around the screen in a palette. The more common elements are right there on the screen; less common elements are tucked away in menus; really rare options are hidden inside dialog boxes. And the best thing about this system is, it's impossible to say something wrong. If you grab a square peg, it will only allow you to put that square peg into square holes. Round holes are disabled so that you can't even try to put your square peg into them.
I help out in this regard by using color-coding in my language. There are seven basic data types in my language (a data type is like an apple or an orange in the aphorism, "you can't mix apples and oranges.") Each data type has its own color. Every element of the language fits into one of the data types, and is drawn on the screen in the color of that data type. This allows you to see instantly what belongs where. If you have a red slot that needs to be filled, you'd better look for a red item to put into that slot.
Another trick I use is ridiculously simple: no acronyms or mysterious contractions. In my language, everything is spelled out. Yes, it wastes screen space, but monitors have been getting bigger all the time; why must we assume that everybody is confined to 640 x 480?
But how does the user of my language know where to start? Well, that's another place where I went to some additional effort. In programming, most commands involve a number of "arguments" that must also be specified for the command to make sense. For example, if you want to set up a programming loop, you'll need to specify the loop index, its starting value, ending value, increment amount, and the boundaries of the loop. If you put in only part of this information, you'll get a syntax error. Therefore, you must remember to fill in all the blanks or you'll get into trouble.
In my language, if you ask for something, the language inserts the item and also puts all its arguments onto the screen at the same time. Those arguements are spelled out and underlined in order to remind you to fill them in with correct values. Here's an example: one of the basic commands in my language is to remove a character from the story, because that character has been killed off. The command looks like this:
RemoveCharacter(_DeadPerson_, _CauseOfDeath_)
DeadPerson is in blue, because characters are always shown in blue; it is underlined, to indicate that you need to tell the computer who died. It's also necessary to tell them the cause of death, which is the event that killed off the person. Events are always orange, and they are specified in one of the menus.
This basic concept of providing default prompting values is applied universally throughout the language. It is impossible to create a system with loose ends left dangling. Whenever something is created, then everything that it requires is created with it. Whenever something is destroyed, then all its accoutrements are destroyed with it. You simply can't screw this thing up -- we won't let you.
This requires a certain amount of discipline on my part as the designer, and the acceptance of some constraints on the part of the user. In one case, for example, I found it necessary to eliminate a minor convenience for the user because it created the possibility of a major headache should the user follow an unorthodox procedure.
Perhaps we should not look on these problems as constraints, but rather concepts that require redefinition. For example, I expended a great deal of time worrying about IF-statements. These are the basic branching constructs in any programming language, and they are fraught with potential for error. After much thrashing about, I found a solution in recasting the problem. Instead of asking the user to cope with an IF statement and its syntax, I used my concept of a role and associated an "activation equation" with the role. If the activation equation generates a TRUE response, then the role is activated. And by the way, did you notice that the previous sentence takes the form of an IF statement? In my language, all subjunctive considerations are swept into the concept of the role, where the implicit IF statement allows us to deal with the issue without the complexities of IF statements. The role-construct is more cumbersome than the general-purpose IF statement, but we're not creating a general-purpose programming language. I believe that roles will address the vast majority of cases that would otherwise be tackled with IF statements.
In much the same way, I started with the conventional concept of a loop and boiled it down to a simpler, less powerful concept that still addresses the great majority of the problems that I expect users to encounter.
Programmers will object that solving the "great majority" of problems isn't good enough -- but in fact I think that it *is* good enough. For too long we have been hamstrung by the belief that a programming language must provide general-purpose solutions to all conceivable problems. That kind of generality is achieved at the price of an over-reduced vocabulary coupled with a fascist grammar. Every language trades off expressive power against utility and ease of learning. For these early stages of language design, we need to consciously accept the need to simplify our languages. When our understanding is perfect, we can design perfect languages. Until then...
Back to my programming language: while I can prevent invalid expressions I cannot preclude incorrect statements. That is, the user of my language can still put together expressions that, while perfectly grammatical, might still yield undesirable results. Even in these cases, I feel a responsibility to provide some protection better than a brusque error message. So I developed a concept that I call "poison". Whenever an expression gets itself into trouble, I poison the results of that calculation and keep going. Thus, the system is designed to recover gracefully from minor errors. There is, of course, a provision for keeping track of these minor errors and advising the programmer of their occurrence, so that the programmer might correct them. But if the programmer doesn't correct them, the system will hitch up its belt and march onward as best it can.
One last example of this philosophy of language design: my spellchecker for tinkertoy text. My system includes a textual output option, which uses text templates created by the user. These templates must follow certain rules, but the rules are enforced by the editor, not by a compiler. The user can type in anything he wants, but the spellchecker automatically scans the text when he's done and cleans up any mistakes. If the user misspells a keyword, the spellchecker will put it right. If the user screws up the syntax of the template, the spellchecker will make corrections to insure that the result follows the rules of the tinkertoy text system. Again, the idea is to utilize the power of the computer to take care of the user, to clean up his mistakes for him.
This is just a single example of the kinds of things that can be done. Because it is my first cut at the problem, I can't recommend this as anything like a definitive solution. But it seems to me that the vastly increased power of modern computers should make possible a vastly smarter language system, one that makes life easier for computer users, not harder. There is so much untapped creative opportunity here, and meanwhile we're spending all our time working on faster video. What a waste!
I would expect that other designers, attacking other problems, could devise other kinds of special-purpose languages that use perhaps a few of the better ideas in this language, but explore new territory untouched in my design. In this manner, as designers learn from each other, we might as a community evolve a basic system of language concepts in much the same way that graphical user interfaces have settled on such common elements as windows, menus, and scroll bars.