The first important thing to bear in mind when designing a
minilanguage is, as usual, to keep it as simple as possible. The
taxonomy diagram we used to organize the case studies implies a
hierarchy of complexity; you want to keep your design as far toward the
left-hand edge as possible. If you can get away with designing a
structured data file rather than a minilanguage that is going to
modify external data when it's interpreted, by all means do
so.
One very pragmatic reason to stick with structured data rather
than a minilanguage is that in a networked world, embedded
minilanguage facilities are subject to abuses that can be inconvenient
or even dangerous. JavaScript is a prime example in the
‘inconvenient’ category; its designers didn't anticipate
that it would be used for pop-up advertisements so obnoxious as to
create a demand for browser features that suppress JavaScript
interpretation.
Microsoft Word macro viruses show how this sort of thing
can become actively dangerous, a security hole that costs billions of
dollars in downtime and lost productivity annually. It is instructive
to note that despite the existence of at least twenty million Unix
users worldwide[95] there has never been any Unix equivalent
of Windows's frequent macro-virus outbreaks. There are a number of
reasons for this, including the fundamentally better security design
of Unix; but at least one is the fact that Unix mail agents do
not
default to executing live content in any
document that the user
views.[96]
If there is any way that your application's users might end up
running programs from untrusted sources, risky features of your
application minilanguage might end up having to be suppressed.
Languages like Java and JavaScript are explicitly
sandboxed—that is, they have limited
access to their environment not merely to simplify their design but to
try to prevent potentially destructive operations by buggy or
malicious code.
On the other hand, a lot of bad designs have been
botched by designers who failed to face up to the fact that they
really needed a minilanguage rather than a data-file format. Too
often, language-like features get pasted on as an afterthought.
The two most common symptoms of this problem are weak, ad-hoc control
structures and poor or nonexistent facilities for declaring
procedures.
It's risky to design minilanguages that are only accidentally
Turing-complete. If you do this the odds are good that, sometime in
the future, some clever fellow is going to think he needs to press
your language into doing loops and conditionals for him. Because
these are only available in an obfuscated way, he'll produce
obfuscated code. The results may be serviceable in the short term,
but are likely to be a nightmare for those who come after him.
Minilanguage design is both powerful and esthetically rewarding,
but it's also full of similar traps. There are kinds of design in
which it is appropriate to take the bottom-up approach of pasting
together a bunch of low-level services and worrying about their
organization after you have explored the problem domain for a
while. One of the virtues of minilanguages is that they can help you
get a good design out of bottom-up programming by allowing you to
defer some top-down decisions into the control flow of programs in
your minilanguage. But if you take a bottom-up approach to the minilanguage
design
itself
, you are likely to end up with an
ugly syntax reflecting a weak language and a poorly-thought-out
implementation.
There are many places in a minilanguage design where small
choices make a large difference in the useability and ease of the
tool:
|
As a language designer, it is a good principle to consider the
alternatives to giving an error message. When there is true ambiguity
in the intent of the programmer an error message is appropriate, but
in many cases the intent is clear, and making the language silently do
the right thing is a great boon. A good example is C accommodating an
extra comma at the end of an array initializer list, which makes both
editing and machine generation of array initializers much easier.
Anti-examples are the pickiness of various HTML readers, especially
their habit of silently discarding parts of your document because of
trivial nesting errors.
|
|
--
Steve Johnson
|
|
On this issue, as elsewhere, there is no substitute for good
taste and engineering judgment. If you're going to design a
minilanguage, don't do it halfway. Declarative minilanguages should
have a clear, consistent language-like syntax designed to be readable
by humans. Imperative ones should add a full range of control
structures adapted from language models you can expect your users to
be familiar with. Think about the language
as
a
language; ask yourself esthetic questions like “Will this be
comfortable to program in?” and even “Will it be pleasant
to look at?” Here, as elsewhere in software design, David
Gelernter's maxim is apt: beauty is the ultimate defense against
complexity.