Programs and buildings are very similar. Both are structural creations composed of smaller elements working in concert to achieve a goal; both are produced in tiny and magnificent scales. Both have spectacular failure modes (the nuclear missile misses its target, the skyscraper collapses into rubble), and both have more subtle failure modes (the program becomes unmaintainable, the condos become a slum).
If you think of architecture as a language, then the attention-getting failure modes are usually errors in the syntax of that language. Perhaps the programmer put a comma in for a period, or the builders misjudged the strength of their concrete beams. The program or building failed not because the overall design was broken, but because of a catastrophic failure in a single element. The story expressed by the structure has an error undermining the whole. The occurrence of this kind of mistake can be minimized by patient, reasoned engineering and lots of CPU time.
Subtle failure modes are much more intriguing, because they're largely immune to presently available math. We can know that a certain wall will hold up a particular load, but once we make it part of a complex system, we become less sure of ourselves. What if the floor underneath undergoes torsion in an earthquake? What if water fills the room, drastically altering the pressure on all surfaces? In general, architects and programmers have sorted out these problems with their gut feelings. Unfortunately, the gut turns out to be highly illogical, based as much on ego and emotion as calculating intellect. As we architect ever-more-critical systems, it becomes increasingly important to move the architecture process from a pure art form to a science based on engineering principles.
The first thing a science needs is a language expressing the concepts of that science. This is the concept of 'patterns'-an explicitly shared language that codifies the basis of our understanding of architecture. By forming a unified, coherent language with which to express our structures, we gain the ability to think rationally about them instead of wishfully attributing the process to art.
For another example, in object-oriented programming, a particularly important pattern is "Loose Coupling," the design goal of ensuring that objects rely upon each other as little as possible. The rationale of Loose Coupling is that if the program's objects must ever be changed or extended or put to other uses, it would be burdensome and inelegant to have to change any other objects. Worse, in a tightly coupled system, you could suffer domino effects, in which change to one object eventually mandates change to all objects.
In architecting homes, a similar, if inverted, pattern applies; humans feel more comfortable in the company of other humans, so the building plan must be designed to accommodate interactions. The ceiling, for instance, might be lower in intimate locations than in passageways; the living room might be designed to put the occupants at ease. Homes without a tight coupling between their spaces and their occupants tend to be distant and unpleasant, like high school cafeterias.
These concepts are undoubtedly simple; the importance of patterns is not that they are individually amazing or insightful, but rather that by setting one down and giving it a name, you gain the ability to mull it over and communicate it to others in a simple and direct way. If everyone working on a program completely understands what the words "loose coupling" mean, then discussions of an object's role can take place entirely in the directness of a pattern language rather than in loose metaphor. This is a big win.
However, in order to be able to use them effectively, it's important to understand that patterns are not dogma. Good pattern languages are like English, or Perl-there's more than one way to express a given thought; and although pattern languages are meant to establish rigor, one eye must also be kept on reality. If you blindly obey every principle of a pattern language, then your words will be awkward, your building will be soulless, and your program will be unimaginative. Each of these consequences can kill just as surely as designing-by-gut; you have to strike a balance between artistry and science.
Pattern languages are excellent for communicating design thoughts to others in an abbreviated, precise way, but much of their strength emerges from the fact that humans can internally use language elements to apprehend complex concepts. This is perhaps the most important aspect of the relationship between pattern languages and Perl. Perl is so permissive that it provides few hints about which approach is optimal, but at the same time, it's rich enough that it invites the programmer to get into extremely complicated situations. As a result, the benefits of using a pattern language to establish a basic way of thinking are many.
Consider, as an example, a project I've been hacking on-simple handwriting recognition using a graphics tablet in Perl. Pen locations stream into the serial port whenever the pen touches the tablet. I would like to be able to write characters on the tablet and have them directed into a window on my screen.
It's especially easy in Perl to get bogged down in the solution to this problem. Machine vision, neural techniques, fuzzy logic, and Hough's transform all loom on the horizon, beckoning the unwitting programmer to her doom. And it's awfully easy to start writing ten-page regular expressions.
But if approached logically, by examining all of the patterns in the programmer's toolbox for suitability, a clear and comprehensible solution falls out.
The approach I took was to use the "Decompose and Parse Elements" pattern. In this pattern, often used by compilers, I broke down all of the elements of the writing action into discrete tokens. So, the action of writing the letter 'A' becomes the following sequence of seven tokens:
token 1: PEN-NOT-ON-PAPER token 2: PEN-DOWN token 3: PEN-MOVING-UP AND PEN- MOVING-RIGHT token 4: PEN-LOCAL-CUSP token 5: PEN-MOVING-DOWN AND PEN- MOVING-RIGHT token 6: PEN-UP token 7: PEN-NOT-ON-PAPER
These tokens can then be made building blocks of a manageable regular expression. The idea that a stream of data can be broken down into a more manageable set of chunks of data and then handed to a parser is a useful pattern, and happens to be an extremely successful strategy in Perl.
To make the process of parsing easier on the eyes, I redefined the name of the tokens (e.g., 'PEN-MOVING-DOWN') to be individual alphabetic characters. You could say that it became "bytecode." Hence, 'PEN-LOCAL-CUSP', which means that the pen made an abrupt change of direction, was changed to 'C'. From this, the token stream for the letter A became
token 1: 0 # PEN-NOT-ON-PAPER token 2: D # PEN-DOWN token 3: u & r # PEN-MOVING-UP AND PEN-MOVING-RIGHT token 4: C # PEN-LOCAL-CUSP token 5: d & r # PEN-MOVING-DOWN AND PEN-MOVING-RIGHT token 6: U # PEN-UP token 7: 0
...which is starting to look like a regular expression. In fact, to detect an 'A' symbol written on the tablet, I say:
$tokenstream =~ /D-ur-C-dr-U/; # PENDOWN, RIGHT&UP, CUSP, RIGHT&DOWN,PENUP
Anything delimited by minus signs happens simultaneously, i.e. -ur- means up and right at the same time. ur would mean an up stroke followed by a right stroke. For performance reasons, the actual parser is a little more complicated than what's described above, but the program can still be described as following the pattern. When thinking about the rest of the program, I can mentally close the door on that part of the code, knowing that it has an assigned job. Further, I can communicate the concept to others with a simple phrase, and the code itself, imbued with the purpose of 'Decomposing and Parsing Elements', becomes clearer.
The one shortcoming of the pattern language paradigm is that, apart from this pleasantly inexpensive magazine, there are few sources of knowledge about pattern languages that aren't stratospheric in price. The seminal texts, Building in the Timeless Way and Pattern Language (Alexander et al.) are must-reads for anyone interested in the philosophy of structure. Several computer-science-specific books have emerged recently, of which Design Patterns (Vlissides et al.) appears to be the strongest. Most of the computer science books tackle the hard problem first, namely Object Oriented Programming Methodologies, but are readable and entertaining nonetheless.
The naming and categorization of idioms is a natural fit with Perl. I strongly recommend that programmers interested in enhancing their structural understanding explore the subject both academically and internally.
Felix Gallo (fsg@ultranet.com) now designs internet appliances for Data General, and will finish Penguin before the year 2000.