>From eernst@cs.washington.edu Sun Nov 8 02:59:06 1998 >From: Erik Ernst >To: crista@ccs.neu.edu, mira@ccs.neu.edu, lorenz@ccs.neu.edu, > lieber@ccs.neu.edu >Subject: Aspects, collaborations, and gbeta >Status: R >Hi, > >cf. some good discussions at OOPSLA'98, here's the comparison between >AOP, Adaptive Plug-and-Play Components, and my work. I think that we >have many common ideas and goals, so it is worthwhile to consider the >differences, and the strong and weak points of each approach. > >Actually, since this became a bit long I decided to set out from some >AspectJ tutorial examples and then try to cover the relation to APPCs >indirectly. > >My work is in the area of language design, and my background is the >Scandinavian OO tradition, starting with Simula in the late sixties >and continuing with BETA from 1975 and on. I'm just about to finish >my PhD programme; my main work is the design and implementation of a >generalization of BETA, called 'gbeta' ("g" for "generalized" and for >"General Public License"). The main point is that gbeta offers a >language integrated approach to the specification and implementation >of collaborations and weaving, hence ensuring all the usual benefits >of seamless syntax and semantics, unified type system and checker, >separate compilation, etc. > >It is my impression that your work, both APPCs and AspectJ, supports a >somewhat richer and more flexible expression of concerns spanning >multiple classes, but at the same time it does not allow for an >equally tight integration with the underlying language(s). It would >seem that textual pre-processing (possibly enhanced with things like a >special aspect/APPC type checker) is an approach which can be >implemented today, but a total integration of the multi-class and >cross-cutting capabilities into the language is a viable long-term >goal. > >My work might be interesting for you because I have taken the opposite >starting point, enhancing a specific programming language in such a >way that concerns spanning several classes can be expressed directly. > >---------------------------------------------------------------------- > >When comparing gbeta to AspectJ, the first difference that comes to >mind is that gbeta does _not_ support the modification of an existing >class. However, it is possible to create a new class from zero or >more existing classes, using an inheritance mechanism which supports >virtual classes and block-structure propagation---concepts that I'll >expand on later---and this allows for expressing and handling >"aspects" in much the same way as would be possible with an aspect >weaver. > >In the following I'll refer to specific examples in the AspectJ >tutorial by page number and class names. I hope that this is a >convenient way for you to see what I'm talking about. > > -- Simple Case -- > >Consider the 'Point' example on slide 26, > > Point { > int _x = 0; > int _y = 0; > void set(int x, int y) { _x = x; _y = y; } > void setX(int x) { _x = x; } > void setY(int y) { _y = y; } > int getX() { return _x; } > int getY() { return _y; } > } > >In gbeta a similar class would be > > Point: > (# _x: @integer; > _y: @integer; > set: (# enter (_x,_y) #); > setX: (# enter _x #); > setY: (# enter _y #); > getX: (# exit _x #); > getY: (# exit _y #) > #) > >Since we cannot change an existing class there is not much we can do >to support 'ShowAccess' right away. In other words, we have specified >that there is no flexibility, and that's what we get. > > -- A Common Case -- > >However, we often specify a more flexible class anyway; a method may >need to be virtual for many reasons, e.g. because it is abstract, and >the flexibility of a virtual method is just what we need for aspects. >Consider this separation of interface and implementation: > > Point: > (* interface *) > (# set:< (# x,y: @integer enter (x,y) do INNER #); (* ":<" = "virtual" *) > setX:< (# x: @integer enter x do INNER #); > setY:< (# y: @integer enter y do INNER #); > getX:< (# x: @integer do INNER exit x #); > getY:< (# y: @integer do INNER exit y #) > #); > > ConcretePoint: Point > (* implementation *) > (# _x,_y: @integer; > set::< (# do (x,y)->(_x,_y) #); (* "::<" = "virtual further-binding" *) > setX::< (# do x->_x #); > setY::< (# do y->_y #); > getX::< (# do _x->x #); > getY::< (# do _y->y #) > #); > >The methods of 'Point' are virtual (can be further-bound to a more >specialized version of the method in a subclass), and they have a >deferred implementation ("do INNER"). We can now write a "ShowAccess" >aspect: > > showWrite: (# do 'W'->putline; INNER #); > showRead: (# do 'R'->putline; INNER #); > > PointShowAccess: > (# set::< showWrite; > setX::< showWrite; > setY::< showWrite; > getX::< showRead; > getY::< showRead > #); > >This aspect can be used to create classes using class combination >expressions like this: > > PointShowAccess & ConcretePoint (* explanation of '&' given below *) > >We can use such class expressions to declare a new class, and we can >use it directly for creation of new objects that we want to observe. >We can also use it for dynamic specialization of existing objects. > >Dynamic specialization of an object is the enhancement of the object's >structure, preserving object identity and state, such that it becomes >an instance of a more specific class, in this case a class that >includes 'PointShowAccess'. This mechanism can be used to add the >'PointShowAccess' aspect to an object which we want to observe. So we >can use this to obtain per-object aspects. > >Note that the aspect is just a class like all others, and the >"aspectness" lies entirely in the class combination which implies >propagation into nested entities, e.g. causing method combination. >(Some more details about the class combination and the associated '&' >operator is given below). > > -- State and Sharing -- > >If we want to access some state of the point then we need to put >the relevant methods into the scope of the aspect: > > PointShowAccess: > (# print: (# do .. getX .. getY .. #); > showWrite: (# do INNER; 'W='->puttext; print; newline #); > ... > #); > >If we want the aspect to have per object state we can declare it in >the aspect (like in all other classes): > > PointShowAccess: > (# count: @integer; > ... > #); > >and if we want to have shared state for the aspect we move it out one >level in the block-structure, > > count: @integer; > PointShowAccess: > (# ... > #); > >We'd use the module system to hide 'count', ensuring restricted access >and avoiding name clashes. If we want to have partial sharing we add >an enclosing layer in the block-structure, > > PointShowAccessSpace: > (# count: @integer; > PointShowAccess: > (# ... > #); > #); > >Then we can create as many "spaces" as we want, > > pspace1,pspace2: @PointShowAccessSpace; (* two "space" objects *) > >and use the aspects from the relevant spaces to create classes: > > Point1: pspace1.PointShowAccess & CartesianPoint; > Point2: pspace2.PointShowAccess & PolarPoint; > >(Assuming that 'CartesianPoint' and 'PolarPoint' are concrete >implementations of 'Point'.) Instances of 'Point1' would share >'pspace1.count' and instances of 'Point2' would share >'pspace2.count'. More levels of block structure can be used to create >various sharing policies, e.g., state shared between groups of groups >of classes. > >Note that 'point1' and 'point2' are specializations of point (and of >'CartesianPoint' resp. 'PolarPoint'), so the polymorphic use of those >instances is allowed, as expected. > > -- About the '&' Operator -- > >The '&' class combination operator supports a form of multiple >inheritance which will do method combination on the enclosed methods, >in the style of the BETA programming language (which is somewhat >similar to CLOS in this respect). For example, if we wanted to observe >writes _after_ the fact we'd use > > showWrite: (# do INNER; 'W'->putline #); > >The "INNER" statement implies execution of the next more specific >contribution to the method, and method "roles" (before, after,..) are >determined by the placement of "INNER". For instance, an 'after' >aspect has the shape (# do INNER; #) and a 'before' aspect has >the shape (# do ; INNER #), where '' denotes some statements, >the "contents" of the method. With the shape (# do ;INNER; #) >we get an effect similar to an around method, and with the shape >(# do ; (if then INNER if); #) we can conditionally >choose to execute the more specific contributions to the method, >etcetc. > > -- Visible and Invisible Aspects -- > >I think it is important to distinguish between visible and >invisible aspects. > >A visible aspect in a class is a part of that class which can be >detected in type analysis and which is allowed to contribute to the >class in any way, e.g., by adding instance variables and methods, >specializing inherited methods, and whatever else the given >inheritance mechanism supports. It is meaningful for a visible aspect >to affect client code because clients benefit from the enhanced >funtionality. The main motivation for using a visible >aspectualization is that the different aspects of a group of classes >can be written aspect by aspect in stead of class by class, hence >yielding a better separation of concerns, and giving the potential for >reusing aspects in different combinations or different contexts. We >could also call this "conceptual aspectualization". > >An invisible aspect, on the other hand, enhances the target classes >with properties that do not affect the type analysis, e.g., by >modifying the behavior of some methods. The addition of state may be >kept invisible if "nobody outside the aspect" can use it. This use of >aspects is especially convenient for debugging and tracing, but it may >also be valuable in other contexts. > >In gbeta, visible aspects are well supported, but it is hard to make >aspects invisible. The available methods use subtype polymorphism: >letting objects of a class that includes the "invisible" aspect be >referred via references whose statically known class does not include >the aspect. The aspect could then be included in the object from >creation or it could be added dynamically. > >The handling of visibility is especially critical if the type system >needs to recognize the existance of a property in a sibling class >for a family of classes which is enhanced with a given aspect. We'll >see an example of this at the end of the next section. > > -- Family Related Classes -- > >The grouping of a set of classes into a family can yield a nice >encapsulation of an aspect which cross-cuts several classes, hence >supporting a convenient switch-on/switch-off of individual aspects in >different usage contexts. > >Consider the following combined example (going down to 1D for brevity, >and referring to the examples on slides 26-48): > > (* First we specify a number of (flexible) classes *) > > Point: > (# init:< (# .. #); > setX:< (# x: @integer enter x do INNER #); > getX:< (# x: @integer do INNER exit x #) > #); > PointImpl: Point(# .. #); > > Line: > (# init:< (# .. #); > set:< (# x1,x2: @integer enter (x1,x2) do INNER #); > setX1:< (# x1: @integer enter x1 do INNER #); > setX2:< (# x2: @integer enter x2 do INNER #); > getX1:< (# x1: @integer do INNER exit x1 #); > getX2:< (# x2: @integer do INNER exit x2 #) > #); > LineImpl: (# .. #); > > Circle: > (# init:< (# .. #); > set:< > (# center,radius: @integer > enter (center,radius) > do INNER > #); > ... > #); > CircleImpl: (# #); > > > (* Then we build various related "packages" of > * classes derived from the above selection *) > > Graphics: > (# Pt:< Point; > Ln:< Line; > Cr:< Circle; > #); > > GraphicsImpl: > (# Pt::< PointImpl; > Ln::< LineImpl; > Cr::< CircleImpl; > #); > > GraphicsLogNewInstances: Graphics > (# log: @file; > init:< > (# do 'log'->file.name; > file.openWrite(# error::(# do .. #)#); > #); > logit: > (# o: ^object > enter o[] > do 'New instance at '+time.toString+' '+o.toString+'\n' > ->log.putline > #); > Pt::< (# init::< (# do INNER; this(Pt)[]->logit #)#); > Ln::< (# init::< (# do INNER; this(Ln)[]->logit #)#); > Cr::< (# init::< (# do INNER; this(Cr)[]->logit #)#) > #); > > GraphicsShowAccess: Graphics > (# showWrite: (# do 'W'->putline; INNER #); > showRead: (# do 'R'->putline; INNER #); > Pt::< (# setX::< showWrite; getX::< showRead #); > Ln::< > (# set::< showWrite; setX1::< showWrite; setX2::< showWrite; > getX1::< showRead; getX2::< showRead > #) > #); > > GraphicsAutoReset: Graphics > (# count: @integer; > Pt::< > (# setX:< > (# do INNER; > (if (count+1->count)>=100 then 0->count->setX if) > #); > #); > #); > > GraphicsAutoResetPerObject: Graphics > (# Pt::< > (# count: @integer; > setX:< > (# do INNER; > (if (count+1->count)>=100 then 0->count->setX if) > #); > #); > #); > >By expressing the graphics classes as a family we get the possibility >of expressing various selections of aspects concisely: > > MyGraphics: @ > GraphicsLogNewInstances & > GraphicsShowAccess & > GraphicsAutoReset & > GraphicsImpl; > > MyPerObjectGraphics: @ > GraphicsLogNewInstances & > GraphicsAutoResetPerObject & > GraphicsAnotherImpl; > >Note that 'MyGraphics.Pt' and 'MyPerObjectGraphics.Pt' are derived >from 'Point', so those clients who do not need to know about the added >aspects can just use them via the 'Point' interface. > >Now consider a setup where the classes in the family are mutually >dependent, like > > observerDesignPattern: > (# subject:< > (# attach: (# enter observers.insert #); > detach: (# enter observers.delete #); > notify: observers.scan > (# do this(Subject)[]->current.update #); > observers: @set(# element::< Observer #); > #); > observer:< > (# update:< > (# theSubject: ^Subject; > enter theSubject[] > do INNER > #) > #) > #); > >In this family, a 'subject' holds a 'set' of 'observer's, to be able >to 'notify' each 'observer' whenever a "significant state change" >occurs. This happens by doing a 'scan' which visits each member of >'observers' and executes the body ('this(Subject)[]->current.update') >with 'current' referring to the currently visited member. > >To use this we need a couple of auxiliary classes, for >instance a 'textBuffer' to be observed by a 'Window' which could >be a 'ColorIcon': > > textBuffer: > (# name: @string; > setFileName:< (# enter name do INNER #); > getFileName:< (# do INNER exit name #); > #); > > Window: (# refresh: (# do INNER #)#); > ColorIcon: Window(# setIconTitle: (# s: @string enter s do .. #)#); > >Now we can create a specialization of the 'observerDesignPattern' >which lets text buffers observe a 'Window': > > WindowAndTextODP: ObserverDesignPattern > (# Subject::< TextBuffer > (# (* ensure that 'notify' is called after changes *) > setFileName::< (# do notify #) > #); > Observer::< Window > (# update::< (# do theSubject[]->getState; refresh #); > getState:< (# theSubject: ^subject enter theSubject[] do INNER #) > #) > #) > >At this level we can do some of the work: When an 'observer' learns >that the 'subject' has changed (when 'notify' invokes 'current.update' >with that observer as 'current') then we can get the state and >'refresh'. We do not know how to get the state, but that's a virtual >method so we can put it in later. Finally we can put create an >instance of the design pattern, 'myODP' and populate it with a subject >'myBuffer' and an observer 'myIcon': > > myODP: @WindowAndTextODP; > > myBuffer: @myODP.Subject; > > myIcon: @(& ColorIcon & myODP.Observer &) > (# getState::< (# do theSubject.getFileName->setIconTitle #)#); > >The icon 'myIcon' has two super-classes, 'ColorIcon' and >'myODP.Observer'. The first is presumably a standard GUI support >class, and the second contributes the design pattern related aspect, >and then the class body containing '(# getState::< (# .. #)#)' >provides the implementation of 'getState' which depends on the type >knowledge that > > 1) 'theSubject' is (at least) a 'textBuffer' because 'myODP' > declares 'subject:< textBuffer..' so it has a 'getFileName' > > 2) current object ('myIcon') is a 'ColorIcon' so it has a > 'setIconTitle' > >This could not be type checked if 'theSubject' in the body of >'getState' would only have the the type declared in the original >'observerDesignPattern', so a notion of tracking the connection >between the types of the members of the family is required, if static >type-checking is desired. For that I assume that a language >integrated approach is of great help, for a textual pre-processing >would hardly preserve the regularity property which allows a >type-checker to recognize the soundness of the above: it would >probably turn into a lot of dynamic casts that just happen to follow >some forgotten structural rules.. > >---------------------------------------------------------------------- > >I hope that this serves to give an impression of why I think that >gbeta can already do a lot of things similar to weaving and APPCs. > >I hope to be able to visit you all* at North Eastern before I return to >Europe. How about 15.-17. December? (I haven't talked to British >Airways about it yet). > >(*) Cristina, you are in Palo Alto, I guess? > > > regards, > >-- >Erik Ernst eernst@cs.washington.edu >Dept. of Comp.Sci. and Eng., Univ. of Washington, Seattle, WA >