μABC: A Minimal Aspect Calculus

Aspect-oriented programming is emerging as a powerful tool for system design and development. In this paper, we study aspects as primitive computational entities on par with objects, functions and horn-clauses. To this end, we introduce μABC, a name-based calculus, that incorporates aspects as primitive. In contrast to earlier work on aspects in the context of object-oriented and functional programming, the only computational entities in μABC are aspects. We establish a compositional translations into μABC from a functional language with aspects and higher-order functions. Further, we delineate the features required to support an aspect-oriented style by presenting a translation of μABC into an extended π-calculus.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Just a very late binding?

To keep the blog rolling, it's time for some, err, nevermind.

In my experience many people tend to dismiss AOP as just a buzzword. Indeed, are aspects anything else except late binding coupled with abuse of the type system? Well, I should not call that abuse, as in the PLs that are typical targets for AOP, the type system usually is effect-blind.

Using the quiet period on LtU, we could probably discuss this minor topic...

[on edit: one of the previously withered threads about AOP]

It is Kiczales...

Just a very late binding?

My short form for it is a very restricted form of compile-time meta-object protocol. (Surprise, surprise)

I don't think it is just a buzzword (there do seem to be some real ideas there); I just don't think it is that interesting from a practical point of view.

Or at least, it doesn't really seem to solve any problems that keep me up at night.

AOP is not restricted MOP

I am really tired of hearing "AOP is just intelligent text processing" or "AOP is a restricted MOP" or something along these lines.

AOP addresses a fundamental problem that neither OO nor functional languages have a good answer to: How can you modularize your code even if you have different aspects in your system that have an incompatible structure?

I think the reason why many FP people don't see the problem that AOP tries to solve is that most software written in FP languages has a very narrow focus (e.g., compiler), without many of these ugly aspects that make writing real business software a pain (heterogenous environments, distribution, customer-specific features, security etc. etc.).

Of course, you can "simulate" all that a tool like AspectJ does with a MOP. Just as you can simulate any other feature by writing an interpreter. You can do everything in every (Turing-complete) language. AspectJ is just a pragmatic first attempt at a more general problem: How to specify crosscutting events in your program execution in a declarative way. For example, there are pointcut languages with which you can state something like "every write access to a field that has been read in the control flow of a previous display() method call" in a declarative and intuitive way.

You could achieve the same with a MOP, but the code would be extremely ugly. It is well-known that MOPs are too powerful and too low-level to be manageable by most programmers. So, it is a good thing that AO languages try to provide only a subset of the possibilites, but in a much more structured and high-level way.

Pointcut languages?

AspectJ is just a pragmatic first attempt at a more general problem: How to specify crosscutting events in your program execution in a declarative way.

So do you see the main value of AOP in opening a new area of PL design, namely languages for defining pointcuts?

I mean, what are the other parts of AOP, except a pointcut language? Can you give to us some pointers that would be both informative and undiluted by marketing?

The first paper I cited in the thread before seems to dismiss a pointcut language design, rushing to formalize advices instead. I would prefer a more balanced presentation. I also don't like seeing aspects coupled too tightly with invocation stack in the second paper. But then again, I may be just reading the papers in a wrong way...

There is more than pointcuts...

but pointcuts are of course one of the key abstractions of most AO languages.

AspectJ also has the notion of inter-type declarations, which allow you to extend existing class definitions with new fields, methods, supertypes, etc. I am not a big friend of this feature, but it is of course different from pointcuts/advice.

There is a big body of work on supporting multiple simultaneous decompositions of software, e.g., subject-oriented programming or Hyper/J and CME by Ossher, Tarr (OOSPLA'93, ICSE'99 etc.) and some other IBM guys, the work on "aspectual components", and also the work on CaesarJ (OOSPLA'02, AOSD'03). Then there is the work from Karl Lieberherr et al on adaptive programming. All these works have mechanisms that are quite different from pointcut/advice. There is a nice paper from Masuhara and Kiczales at ECOOP'03 that compares some of these different crosscutting mechanisms and tries to provide a unifying framework for it.

In my opinion, the theoretical works on AOP that I am aware of (and that have been discussed on LTU) are too specific to AspectJ-like AOP and hence disseminate a slightly warped picture of AOP.

AOP vs. OOP

I am really tired of hearing "AOP is just intelligent text processing" or "AOP is a restricted MOP" or something along these lines.

Just to clarify, my description was not intended to be a disparagement, but to give a feel to a PLT enthusiast what AOP was about.

Restricted is often a positive attribute, especially in math and CS.

And I'm not even a MOP fan. ;-)

I think the reason why many FP people don't see the problem that AOP tries to solve is that most software written in FP languages has a very narrow focus (e.g., compiler), without many of these ugly aspects that make writing real business software a pain (heterogenous environments, distribution, customer-specific features, security etc. etc.).

I should also clarify that FPer and PLTer is just my superhero identity; during the day, I'm a professional Java programmer who builds just the kind of messy, distributed, etc. systems you describe.

It is exactly those kinds of problems that I don't find AOP solves particularly well, compared to simpler, less "meta" design techniques.

Take the oft cited "security policy" or "trace logging" aspects. In my experience, these require case by case consideration to be effective but not onerous. Adding logging or security checks non-locally with a blanket "policy" generally doesn't do what you want and makes it harder to reason about the behaviour of the program.

If your are using OOP, you already have a host of design and abstraction techniques to handle this kind of thing (that is what OOP is supposed to be all about, right?) that don't require adding a second layer of processing or reasoning.

AOP is certainly worthy of research, and may have some niches I'm unfamiliar with where it is quite useful, but I don't think that AOP is about to (or should) "sweep the programming world" the way some have been predicting for a few years now.

AOP Critique

Klaus Ostermann: AOP addresses a fundamental problem that neither OO nor functional languages have a good answer to: How can you modularize your code even if you have different aspects in your system that have an incompatible structure?

Ironically, I tend to think that this is exactly the problem addressed by functors in ML's module system.

Klaus: I think the reason why many FP people don't see the problem that AOP tries to solve is that most software written in FP languages has a very narrow focus (e.g., compiler), without many of these ugly aspects that make writing real business software a pain (heterogenous environments, distribution, customer-specific features, security etc. etc.).

I tend to hear this line of argumentation a lot from people who don't, in fact, use functional programming. Even a cursory glance at the O'Caml Humps, for example, will demonstrate that this just isn't the case.

Like Marc, I'm a ML/Lisp/Smalltalk maven in my spare time, and have been writing C++ and Java for a living for more years than I care to count. In fact, just yesterday I was working on a web app in Java in which I'm using my preferred framework, WebWork. One of WebWork's features is "Interceptors" as described in this talk, where they are referred to as "practical AOP." It's telling that "Interceptors" in WebWork are neither more nor less than :before, :after, or :around methods in CLOS. It's also telling that Mike Cannon-Brookes felt the need to qualify "AOP" with "practical," being familiar with efforts such as AspectJ, Aspectwerkz, Nanning... It's also telling that I have yet to use even that level of AOP (apart from the extent to which the framework itself uses it) in my own web apps, some of which have been real monsters using Hibernate for persistence, JMS, JNDI, JCE, even JNI to call out to native code...

I can't help but think that AOP is what you get when your low-ceiling language (Java) has finally become so intolerable that some kind of escape valve becomes necessary. No one yet (IMHO) has made anything remotely resembling a compelling case for it that isn't also a compelling case for using a different language that doesn't exhibit the shortcomings that make AOP desirable in the first place. Since AOP almost always comes up in the Java context, and Java is overwhelmingly used in web application contexts, this is particularly silly: as Paul Graham pointed out, no one cares what your web app is written in, so use a good tool. These days, there are many good Smalltalk, Lisp, ML, Python, Ruby... web app frameworks. For example, Ruby on Rails is a revelation of easy web app development with nary an "aspect" in sight.

AspectL / AspectS

As a fellow Smalltalk / Lisp user, these two frameworks tell me exactly what the languages aren't providing that AOP can - and they're in fact a hell of a lot more useful than AspectJ. (Note: they do indeed form a MOP library.) See AspectS and AspectL.

e.g. ...

As a fellow Smalltalk / Lisp user, these two frameworks tell me exactly what the languages aren't providing that AOP can - and they're in fact a hell of a lot more useful than AspectJ.

Can you tell us what practical problems you have run into that the frameworks solve, and how existing capabilities in those languages are insufficient to the task?

Notes on AspectL

AspectL was an indeed an attempt to see what AspectJ-style AOP brings to the table. AspectL turns them into four separate features.

  • Generic pointcuts: Generalizes pointcuts in a similar way as generic functions generalize methods. I.e., you can add new join points at runtime.
  • Destructive mixins: Allows adding slots to an existing class, or changing the list of direct superclasses, and some other things. (This is what the AspectJ guys call inter-type declarations.)
  • Special classes, i.e., classes whose slots can be rebound with dynamic scope.
  • Special functions, i.e., generic functions whose sets of methods can be rebound with dynamic scope. (An extension of my dynamically scoped functions.)

By now, I think that the first one is mostly useless. Generic pointcuts don't add a lot when you already have macros and user-defined method combinations at your disposal. I am also very skeptical about destructive mixins. However, that feature is the one I have gotten most positive feedback for by Common Lispers who would really like to have it. (I think destructive mixins are not declarative enough, and I am still working on alternative designs.) I am pretty convinced that the dynamic scoping stuff is very useful. The special functions have one minor flaw, though: They don't behave very well together with methods that recursively call the generic function to which they belong. I know how to solve this but haven't had time to implement this yet.

Pascal

No compelling case for AOP...

Maybe this paper convinces you? Take this little old display update example that is used in this paper. It is very simple but also very illustrative. I would be interested to see your solution to this problem in whatever language you like.

Possible solutions

Take this little old display update example that is used in this paper. It is very simple but also very illustrative. I would be interested to see your solution to this problem in whatever language you like.
Referenced Paper

I'm not sure I find the non-AOP example all that objectionable. (I would probably abstract a method

void updateDisplay(){
   Display.update();
}

into the base class to improve modularity, but that is about all)

I'm also unclear what you gain in conciseness. You eliminate 4 lines and gain 4+, and the lines you get are not where you expect them to be if you are looking at the individual methods, thus making it harder to see what will happen when you execute them.

If the situation got more complicated, I would refactor the code to encapsulate groupings of common calls.

I guess that's the thing: I use TDD, and where AOP would graft on an aspect to retrofit a design, I would rather refactor my design to incorporate the new functionality.

Furthermore, this example shows the pointlessness of a general "policy" of display update: when the display should update is in fact an important part of my implementation decisions. It may not always be efficient or appropriate to update as soon as I change the data. I may want to do a bunch of work before updating.

Using normal method calls in the appropriate methods is entirely appropriate for this case, IMO.

So you don't want to modularize?

This is the whole point of AOP: Modularize design decisions. If you prefer your code tangled, then AOP is useless. However, then you also have to deal with the disadvantages of your tangled code, like reusability, maintainability etc.

One point that the paper makes very clear is this: If you are using the observer design pattern, you are basically enumerating the relevant points in the code where an update occurs. With a pointcut language, you describe the *intention* behind it: What do these different places have in common?

It is the same difference as between the set {2,3,5,7,11,13,17,19} and { n | n

I do modularize

This is the whole point of AOP: Modularize design decisions.

I already do modularize design: by using inheritence, delegation, interfaces, containers, decomposition, etc that are already there in OOP.

If you are using the observer design pattern, you are basically enumerating the relevant points in the code where an update occurs

How is that different from enumerating the cross-cut points in the aspect? Besides I rarely "use" a pattern anymore; I prefer to let patterns emerge naturally from other design criteria.

They are the same sets, but the latter one describes the intent, whereas the former only the extent.

I'm not sure this set example maps to the code. If my method is called "updateDisplay", it is pretty clear what the intent is within the local context where it is called. The details are abstracted into the implementation.

If I have an external aspect called UpdateSignalling that enumerates where this is done, I'm not sure how this helps me to understand my program in local context. I now need to know the internals of TWO abstractions (aspect and class) to understand what is going on. That is a REDUCTION in modularity.

But...

But what if you want to, say, reuse the figure code in a context where you do not need updating? Then the display updating code suddenly gets in your way...

The point with the "pattern" is that you don't enumerate the relevant points in the code but instead describe the defining property of these points. AspectJ's possibilities in this regard are limited, but the general idea is very powerful, I think.

Well...

But what if you want to, say, reuse the figure code in a context where you do not need updating? Then the display updating code suddenly gets in your way...

This tells me that the setter methods are the wrong place to put the display update code.

For example, the simplest way to fix the code example for this scenario is to add the updateDisplay as a public method in the base class, remove the internal calls to the update, and then make it the responsibility of the caller to decide when and if to update the display.

There are a bunch of other designs that can handle more complex situations, depending on the combination of requirements you actually have for a given project, just using OOP methods.

The point with the "pattern" is that you don't enumerate the relevant points in the code but instead describe the defining property of these points. AspectJ's possibilities in this regard are limited, but the general idea is very powerful, I think.

Powerful does not equal good. (The flip side of restricted does not equal bad.)

It seems to me that you need you either need to enumerate cases or add name mangling to your code if you want to have some kind of pattern matching capability powerful enough to apply always and only at those points you want it to.

Wouldn't it be easier to just put functionality where you want it and refactor your design if you find a lot of duplication as a result?

Would not work in a component-based setting

There are many arguments against changing/refactoring your code, one of them is that this approach would not work in a component-based setting, where different vendors are responsible for different components of your code.

I think one reason why this old component dream from the famous 1968 NATO conference did not work until now are exactly these crosscutting aspects - they prevent large-scale reuse.

Baloney! (Or whatever other sandwich meat you prefer ;-)

There are many arguments against changing/refactoring your code, one of them is that this approach would not work in a component-based setting, where different vendors are responsible for different components of your code.

I don't see how aspects solves this for you. If you have access to the source code to be able to specify your AO behaviours at that level, you can just as easily refactor.

If you don't have the source code, you can just as easily write wrappers around the third party calls to add/control the behaviour you want.

Even if this were not true, large-scale reuse is IMO a pipe-dream dependent on the idea that all problems of a class have the same parameters and requirements, and that yesterday's requirements are the same as today's.

If this were true, all programmers would be out of work and there would be no need for large-scale reuse anyway. ;-)

I prefer ham

but I have a different opinion ;-)

Aspects help you do this because you don't have to change the code. It is true that, with current AO languages, you sometimes need to know the source code in order to define your aspects correctly. But there are some interesting works in the direction of aspect languages where this does not need to be the case, e.g., by more sophisticated interfaces or by more abstract pointcuts. Even if you need to know the source code, there are still important benefits by not having to change it, e.g., w.r.t. new versions of the component.

And you *can't* achieve the same behavior with wrappers. For one, you can only intercept single method calls and you can't quantify, like with a pointcut. This makes wrappers ugly. Second, you can't intercept internal actions that don't go through the wrapper. There are other problems like maintaining wrapper identity etc. There is a nice paper by Urs Hölzle that summarizes these problems.

Extra fat bacon

Even if you need to know the source code, there are still important benefits by not having to change it, e.g., w.r.t. new versions of the component.
If I needed to know the source code, it means I depended on some fact about it. Nobody guarantees this fact will hold in the new version - unless this fact is a part of the component's contract. Now we are getting closer...
there are some interesting works in the direction of aspect languages where this does not need to be the case, e.g., by more sophisticated interfaces
Does it mean exposing the join points of the component?

I start to understand AOP as a union of:

  1. the way for exposing join points (type theory?)
  2. the way for combining these join points (declaratively?) - pointcuts
  3. the way for binding some functionality to pointcuts - advices (is this somehow special to AOP?)
On the theoretical side, I don't see anything new here. On a pragmatic side, the big numbers come into play. There should be much more join points per component than say methods per object in current OOPLs - a challenge for type systems, the vast majority of join points will be unbound - a challenge for optimization, and probably more.

Wrap up

And you *can't* achieve the same behavior with wrappers.

I read the paper and, to be frank, I found all of his concerns specualative. Part of the problem is Hoelzle is assuming that reuse is more important than other considerations.

If I had an application where reuse would involve creating ever multiplying wrappers just to work, I would take the lesson that the code I was trying to reuse was NOT really that suited to my problem, and look for different solutions.

Sometimes in software engineering, people spend a lot of time searching for holy grails of technology that will magically solve the problems and difficulties of development.

I suspect, despite the arguments put forth so far, that AOP is one of these ever-receding illusions.

Looks like a monad to me.

I've said before that OOP lets you combine code with given semantics, and the monadic interface lets you combine code with whatever semantics you want. It seems to me that AOP is just that, a way to extend the semantics of concatenation of OOP code. Since I've already got monads, and they have other properties that in my opinion makes them superior to OOP, I don't see any advantage to OOP plus AOP.

To implement the display update example, I would just lift my existing code through another monad that does the display updating. For details check out the monad transformers section of the nomaware tutorial.

I do have a return question: Arrows give me first class modular semantics, allowing me to do a wide range of optimizations not available in monads. Does AOP have an equivalent that allows the programmer to operate on the aspects themselves as values? Are the most basic pointcut aspects built from smaller modular pieces allowing users to extend them in the program itself? Basically, is the whole of the parts of the aspect weaving process available to the programmer?

--Shae Erisson - ScannedInAvian.com

Monads

I would be extremely curious to see how the display update example would look like using monads. Could you sketch the solution?

Execution Monad in Scheme by Wand-Kiczales-Dutchyn

I hacked on an AOP monad for a few minutes, but realized it would be more sensible to ask google about the connection between monads and AOP.

Happily, Wand, Kiczales himself, and Dutchyn wrote a paper about it. Wand's list of AOP related papers includes a link to a more formal semantics of AOP where an Execution monad is defined in Scheme.

Also, since I was at ICFP03 and heard Mitchell Wand's talk, it's likely that's where I heard that monads and AOP are related.
I wonder if an ExecutionT monad transformer would be useful for interpreter or compiler writers.

--Shae Erisson - ScannedInAvian.com

Monads in Wand's paper

As far as I understood this paper, they are using monads to make the denotational semantics itself more modular. But this general technique (I think it is from Moggi) is used for many different (non-AO) denotational semantics. Does this show any connection between AO and monads?

monads

There is an oft-cited paper that AFAIK was the first published suggestion of a connection between monads and aspects:

Wolfgang de Meuter, Monads as a Theoretical Foundation for AOP, 1997.

The most immediately apparent connection between monads and aspects is that they both model global effects that can change the meaning of local expressions. But it's a fuzzy connection at best.

One difference is that monads model global extensions to a language, whereas aspects describe extensions to one program. Sure, Haskell obscures this fact by making it look like monads are something programmatic, but they're really mathematical entities used (in our line of work, anyway) for modeling PLs. When you program with the State monad in Haskell, you're writing a little interpreter for an embedded language in Haskell with mutable state.

The Wand et al paper models a dynamically scoped set of join points via a monad, with standard monadic operations for installing advice. By contrast, de Meuter suggests a kind of "user-programmable" set of monadic operations, trying to treat bind itself as a join-point. The latter is problematic, because it's really not a monad; it's just some code with procedures called unit and bind.

Feature interaction

Christian Prehofer also wrote about it in 1997 in From Inheritance to Feature Interaction, or Composing Monads. I don't remember if he uses the words "aspect oriented", though.

Display update in Common Lisp

Here is the code for Common Lisp.

;; The base code. Nothing about display updating mentioned here.

(defclass point ()
  ((x :accessor x)
   (y :accessor y)))
; Note: :accessor defines setter and getter methods.

(defmethod move ((object point) dx dy)
  (incf (x object) dx)
  (incf (y object) dy))

(defclass line ()
  ((p1 :reader p1)
   (p2 :reader p2)))

(defmethod move ((object line) dx dy)
  (move (p1 object) dx dy)
  (move (p2 object) dx dy))

;; The display updating stuff. Could be in a separate file.

(defvar *display-update* t)
; Note: Global variables are dynamically scoped.

(defun maybe-display-update ()
  (when *display-update*
    (display-update)))

(defmethod (setf slot-value-using-class) :after
  (new-value class (object point) slot)
  (maybe-display-update))
; Note: This defines an :after method on low-level slot/field access.
; (Imagine Java's "=" were a method.)

(defmethod move (object dx dy) :around
  (let ((*display-update* nil))
    (call-next-method))
  (maybe-display-update))

"It is well-known..."

It is well-known that MOPs are too powerful and too low-level to be manageable by most programmers.

I don't know about any studies that show this. I think this is similarly well-known as it is that dynamic typing leads to buggy programs. That is, it's probably just a widely-held prejudice.

Pascal

disparagement of AOP

One reason why AOP may be met with such derision is that it is patent-encumbered.

Ugh...

Like many patents, the language of the claims is sufficiently obtuse that it's hard to tell what may or may not infringe. OTOH, it seems to me (IANAL) that there is so much prior art (even if not called "aspect oriented programming") out there that I don't know what they possibly could claim.

Among the technologies (predating the filing of the patent) that have been used for aspect-stuff, and examples:

* CLOS meta-object protocol (and other meta-object protocols), with things like function wrappers and the like being used for cross-cutting concerns

* Hook functions in numerous application frameworks

* Virtual hook functions (functions which do nothing in a base class; are often declared "protected" in languages with compiler-enforced encapsulation, and which are intended to be optionally overridden by a subclass, to provide some orthogonal functionality).

* Manual use of overriding (in OO systems) to wrap functions with aspects.

* Use of "policy objects" to implement an aspect

* Allocators in the C++ standard template library (Well-known early use of generics and template metaprogramming as an means of implementing an aspect). Note--I'm not familiar with the original (Ada) implementation of STL, so this may even predate the C++ implementation.

* Various advanced languages out there that support "class algebras" (allowing class definitions to be treated as algebraic objects, including arbitrary removal and replacement of features. C++/Java-style inheritance is a subset of this; with Eiffel having a bigger subset).

Given all that--what's new under the sun? The notion that these technologies may be used for domain-orthogonal ("cross-cutting") concerns? Tools (code weavers, etc.) for implementing aspecty transformations, especially for weak languqges (like Java!) with little or no support for such transformations within the language?

And has Xerox stated how they intend to use this patent (and the numerous others that have probably been filed but not yet granted?) I would hate to see them screw up this interesting idea in the manner which they screwed up Smalltalk...

Given how much patent litigat

Given how much patent litigation costs in the USA (Patti Waldmeir's most recent piece in the Financial Times gives $4 million as the average cost of defending an infringement claim), whether or not a patent is sound is not so relevant, unfortunately.

On a positive side

...I find A Theory of Aspects quite readable and would recommend it as an AOP introduction material for guys who are into theory.

Retrospect: Dynamically scoped functions vs. AOP

Retrospect: Perspectives (including program slicing) and AOP

Perspectives.

I only hope linking (subjectively) relevant threads together is a good thing...