Thoughts on Aspect Oriented Programming

In November 1996, Gregor Kiczales' and Karl Lieberherr's student, Cristina Lopes Cristina Lopes defended her thesis proposal "D - A language famework for distributed programming" at Northeastern University. Gregor also talked on "Separation of Concerns" the main theme behind Aspect Oriented Programming [AOP] and Open Implementation [OI]. This is a summary of some ideas from the talks (and elsewhere). I have hardcopies of the slides presented at the defense. Also, Crista's home page references several interesting papers on GOOP (see below) and AOP, among others.

Failure of abstraction

Computer scientists are taught that abstraction is important. Examples of abstraction techniques include:

There are many other examples. Abstraction also underies many computer science terms such as "a layered architecture " and "a black box".

While such techniques are powerful and important, Dick Gabriel and Guy Steel have argued that modern computer languages have failed to provide the kinds of abstractions programmers really need. Heres an example:

Consider the C++ code fragment that adds three three-element vectors:

Class Vector {...} // 3 element vector. Vector a, b, c, d; ... d = a + b + c;

Current C++ compilers compile this into three loops, one for the "=" and each of the two "+" operations. This is significantly slower than what can be done by hand by merging the three loops.

About 95% of the performance can be recovered by using a sophistocated template scheme [Blitz++]. Another way to combat this problem is to use a minilanguage, such as Rich Waters' Series [Richard C. Waters: Automatic Transformation of Series Expressions into Loops. TOPLAS13(1): 52-98 (1991)] system in Common Lisp. It can compile loops expressed in a expressive functional style into an efficient iteration. Either way, the language itself is not providing the kind of abstraction one needs.

Tangling of concerns

Consider a second example of Floodgate class in a multithreaded, possibly distributed environment:
Class Floodgate: public Lockable ... { public: void open(); bool isOpen() { return isFloodgateOpen;} void close(); private: bool isFloodgateOpen = 0; void reallyOpen(); ... } void Floodgate::open() { lock.grab(); if (!isFloodgateOpen) { try { reallyOpen(); isFloodgateOpen = 1; catch (...) { lock.release(); throw; }} lock.release(); }

In addtion to really opening the floodgate, the Floodgate::open() method must grab and release a lock, check and update a state variable, and handle an exception.

Often the software we write attemps to deal with several concerns at once. As in the Floodgate example, concerns are often tangled together. Lopes shows a real example of an ILU C function to create a document in a distributed documentation system. The eight operations that are required take 40 lines that are almost completely devoted to exception handling. What's worse is that the entire program looks like this.

Alternatively, as in the Vector example, a concern that is not addressed in the software can lead to a performance problem. Hand optimizing the software removes the performance problem but leaves a tangle which may need to be repeated throughout the program.

Separate concerns, but not completely

The main idea behind aspect oriented programming, AOP, is to allow a program to be constructed by describing each concern separately. For example, Lopes' language, D, consists of four languages:
OIL - Object implemenation language
a subset of Java (with some additions) for describing objects and methods.
Goop - Communication Language
describes how remote objects are communicated. For example, in a distributed library application, when a book is returned to a remote application only its title, author, ISBN number, and year of publication are provided. Other aspects of the book, such as the list of copies the library has, require a remote invocation. Goop uses an adaptive programming approach that allows such requirements to be specified without committing to too many details about the class hierarchy.
Cool - Coordiantion Language
specifies coordination between objects, for example which opeartions cannot happen concurrently, using autoex and mutex relationships. Also, state variables, such as isFloodgateOpen can be defined. State transitions can occur before or after method invocations.
Act - Activity Language
creates objects and gets concurrent activities going, like a main() program in C.

Since each sublanguage is devoted to a relatively few concerns, its syntax remains relatively simple. For example, the BNF rules for Cool's syntax requires only 15 lines, and Act's requires 10.

Also, your software becomes more compact because more aspects of the program are specified declaratively and at a meta level. For example, one can specify which operations cannot occur simultaneously in one line.

While it is important to separate concerns, it is not possible to separate them completely. An aspect specified on one sublanguge may occasionaly require reference another defined in another sublanguage. This residual tangle should be relatively small.

Aspect Weaver

One way to think of a programmer's task as writing "shreds" of a program that must be combined and executed (glued together) by some "engine". For example, in an object oriented language the shreds are things like classes and methods and the engine is a compiler. In Prolog, the shreds are rules and the engine uses unification pattern matching and chronological backtracking. Effective programming requires not only knowing how to write the shreds but also knowing something about the workings of the engine. This particularly true in high level languages since much more is happening behind the scenes. For example, effective use of C++ requires understanding how such details as object construction and destruction interact with argument passing. Also, effective Prolog requires using the cut operation to control backtracking.

In AOP, the aspects and their components are the shreds and the engine is called the"aspect weaver". The belief is that workings of the aspect weaver will be relatively easy to understand because the weaving happens at the code level (at least in D) and the results of the weaving are observable. It should also be easier to provide high performance implementations since the weaver is concerned with many fewer issues than an optimizing compiler would be.

Where's the MOP?

The first thing I wanted was my own weaver. To make the weaver customizable, so one could add new aspects for example, one would need metaobjects and rapid parser construction at least. Gregor says these things will happen after they've done a couple of weavers.

AOP allows a programmer to specify behavior at the meta level. However, it does not (yet) provide a way of defining new aspects. Thus while AOP is in line with the Open Implementation philosophy it is a bit of a step back from it.

This may be important for acceptance. Programmers are already familiar with code generation systems, such as Orbix (that addes CORBA distribution to objects) or Objectivity (that adds persistance). Also most applications are built out of one or more minilanguages (such as yacc, lex, perl ...). An AOP system, such as the D language, should be fairly familiar to the main stream.

Gabriel says that it takes 25 to 30 years for an idea to make it from the laboratory to established commercial use. Open Implementation is roughly 10 years along this path. It orignally started in the area of language design but is now being used on distributed systems, operating systems, and transaction processing. Also, concepts like reflection and introspection are even influencing such languages as C++ and Java. Aspect oriented programming should make an important contribution since the number of concerns our progams have doesn't seem to be decreasing.