The Key to AOP
History shows that to be widely adopted, new programming paradigms must be expressive, efficient, intuitive, compatible and have good tool support.By Gregor Kiczales
As far back as 1956, we’ve been inching our way up to program at higher levels of abstraction: assembler, structured programming, object-oriented programming, type safety and so on. This history suggests that for a programming paradigm to achieve broad acceptance, it must be expressive, efficient, intuitive, compatible and have good tool support.
A programming paradigm is expressive if it lets you write code that clearly indicates what you want it to do. One aspect of structured programming was to replace the unstructured goto construct with if, do, while, procedures and other similar constructs. “The Evolution of Expression” shows that code written with these constructs is more expressive.
There’s a bit of a paradox here: goto is more powerful than the structured mechanisms, and the change made the languages larger. But it also made programs written in the languages more clear. In fact, expressiveness often comes from limiting power and adding constructs. The constructs of a statically typed OO language are less powerful than those of a homegrown OO framework in C might be.
Why do changes like these make languages more expressive? Because we add a small number of constructs that explicitly capture the right idioms, are compositional and support abstraction.
By the right idioms, I mean things that developers want to do and that produce good code—for example, procedure call/return rather than unstructured jumps. Compositional means that you can combine the constructs—you can put an if statement inside a loop, one procedure can call another, objects can aggregate and so on. Supporting abstraction means that you can treat a composition of constructs as a black box—you can use one procedure without knowing what’s inside it. Taken together, the individual constructs capture what you want to do and let you build them up into larger and larger structures that remain comprehensible despite their size. This is what makes a paradigm expressive.
Programmers won’t use a new paradigm unless it’s efficient. Efficiency is a complex issue, but two rules of thumb generally describe the necessary criteria. Pay as you go means that the new constructs have no cost if you don’t use them. Comparable to hand code means that code written using the new constructs is about as efficient as if you coded the same functionality without them. These criteria reflect that, as a developer, you’d be reluctant to use a mechanism that costs more than it gives back. You don’t want a construct that appears only in part of your code to slow down the entire program; thus, new paradigms usually must meet the pay-as-you-go criterion right away. However, you’re more willing for a new construct to be modestly inefficient, at least while it’s still evolving; thus, new paradigms generally get a little more time to meet the comparable-to-hand-code test.
A programming paradigm is intuitive if, when you see the first examples of how it works, you begin to understand it and see how it might be useful. This doesn’t mean that no training is required; just that the new paradigm is relatively easy to learn and relevant.
The history of OOP highlights the way intuitiveness can change over time. Even though OOP was invented in 1964—before Dijkstra’s famous structured programming letter (but not before Algol)—it wasn’t until C++ in the ’80s, or even Java in the ’90s, that OOP really took off. I attribute this to the fact that OOP’s inventors saw too far into the future. In the ’60s and ’70s, most programmers couldn’t relate to OOP: It solved problems that they either didn’t have or didn’t know they had. But by the ’80s and ’90s, the OO idea was “in the air,” so that widespread adoption of C++ and then Java became possible.
Intuitiveness often distinguishes technically sound niche mechanisms from those that gain widespread adoption. Functional programming is expressive and efficient, but at least for now, it isn’t a natural way of thinking for most developers.
Compatibility and Tool Support
Compatibility concerns how gracefully you can incorporate a new paradigm into existing practice. In addition to being timely, C++ blossomed because, in part, it was C-compatible. In fact, because the first implementation was a pre-processor rather than a native compiler, C++ was so compatible that it had the “you can use it without telling your boss” property.
Sometimes it’s possible to introduce an incompatible platform, as was done with Java. Even then, compatibility is still important; the Java language syntax made it programmer-compatible, which helped its adoption. But more often, platform compatibility is a criterion for mainstream adoption. One reason Lisp struggled is that most implementations wanted to be the platform rather than fitting in compatibly with other code.
New paradigms improve expressiveness by letting you structure your code in new ways, but you also want the supporting tools to help you work with that structure. You want to see what methods a class has, which are inherited, which are overridden and so on. You want to know what arguments a method accepts and what type it returns. When debugging, you want to be taken to the right place in the source for an error.
It’s easy to take all these features for granted now—especially when we see how beautifully modern IDEs present that information. But this technology had to be invented to make structured and OO programming viable. Smalltalk was a true turning point in this regard—the Smalltalk browser let programmers really see the class structure of their code. Each new programming paradigm must provide its own appropriate extensions to tool support.
New programming paradigms typically add a small number of constructs to our evolving bag of tools, and these constructs improve expressiveness when they’re minimal, useful, compositional and support abstraction. To achieve mainstream adoption, they must also be efficient, intuitive, compatible and come with good tool support. Expressiveness and intuitiveness come first; they get people interested. Then, the early adopters drive efficiency, compatibility and tool support.
Next month, we’ll rate aspect-oriented programming against these criteria,
to get a sense of what its proponents must do to ensure its success.
Gregor Kiczales led the Xerox PARC teams that developed aspect-oriented programming and AspectJ, and is a leading AOP evangelist