Hi Dean: thank you very much for your experience report. Your feedback is very valuable. My response is below. >From dta@alexandria.synquiry.com Fri May 8 10:24:35 1998 >From: Dean T Allemang >To: lieber@ccs.neu.edu >Cc: dta@alexandria.synquiry.com >Subject: Using Demeter in ZD > >Hello Karl, > >I have just finished a little project that I think might interest >you. Here is the background: > > >One of my recent goals has been to make it easier to create ZD >representations. Toward this end, I have looked into using the Acme >tools for creating input to ZD. This seems reasonable, because ZD >describes how things are composed, and does it using some >box-and-arrow diagrams, and a sort of inclusion principle, all of >which are handled well by Acme. Also, Acme provides a reasonable >number of tools for manipulating, exchanging and viewing things in >Acme. > Good that ACME helps you. > > >There is a problem, however, of translating Acme into the other >languages, in this case, ZD. There are three parts to this: > >1) write a style in Acme to express what you want to express, > >2) figure out how systems expressed in this style map to your target >language (and back), > >3) write a translator to do the mapping. > > > >Now, since Acme is so free-form, there are a lot of ways to do (1) and >(2); I can pretty much define any style I wish, and then think about >how to map it to ZD. There is even good reason to define more than >one style, depending on how I think it is convenient to view the ZD >models. > > >This means, however, that one has to keep repeating (3) over and over, >for each choice. > > >Now, think for a moment what (3) entails; there is a class model that >relates to Acme (the Acme style), and another class model for ZD, and >you need to write code to generate ZD from an Acme model. Given that >the Acme model might change, you would like your code to adapt to >these changes. > > >So, it seemed to me, this might be a good job for Demeter. Since I >had been playing around with Ken's dem.lisp, so I knew more or less >how it worked, I decided to give it a try, and wrote the translator in >Demeter. > > >I have a few observations on the experience, as a sort of demeter >outsider (I have only been to one of your meetings, and have just read >the first part of your book (it being so C oriented, I find it a bit >disorienting)). I hope you find them interesting. > > >The first problem I had was that one of the paths that I wanted to >generate along was rather hard to express in dem.lisp; basically, >there was a list of objects pointing to one another, with a special >object marking the start and the finish. The correct path through the >class graph, as it turns out, had the form something like this: > >A B C D E F G C D E X > >That is, C, D and E appeared twice in the path. This caused a bit of >trouble, since dem.lisp was arranged to avoid any kind of cycles in >the graph. > >I tried to fix this by allowing nodes to appear twice in a path, but >the search space became so large that I never got dem to finish >generating anything. > >I fixed this by observing that the path above can be seen as the >catenation of two paths, namely: > >A B C D E F > >and > >G C D E X > >neither of which has any duplication in it. So I modified dem.lisp to >consider the first allowed link (in this case, F->G) as special; and >to break the problem into two smaller ones, namely start-->F and >G-->end. This is actually a little bit faster than normal dem (though >I didn't notice the difference), and allows certain kinds of cycles. >You will notice that this solution is just a special case of the >general graph solution that you described in the paper that I reviewed >last year; I think this situation would present no difficulty at all >for the demeter described there. > A B C D E F G C D E X is an interesting path. The traversal specification capability in dem.lisp is limited since it does not implement general traversal strategies. As you point out, Demeter/Java which implements strategies does deal with such short-cut situations correctly without requiring the user to split the path as you did. We also have identified that strategies should be specified hierarchically for dense class graphs. > > >Another interesting feature was the fact that in CLOS, methods are >dispatched dynamically at run time. So it is perfectly meaningful to >specify in dem.lisp (I don't know how the syntax of this would go in >normal demeter): > >(define-class effect (proxy) > ((getparent state) > (port precondition) > (port final) > ) > ) > > > >Notice that the arc "port" has been defined twice, with two different >types. The way this works is that an effect is part of a chain of >things; when the "port" of an effect is final, the chain ends (see the >class "X" in the example above). It turns out that Ken's dem.lisp >handles this case with no modification necessary, and the correct >methods are generated, and dispatched at runtime, depending on the >actual content of the slot "port". > > > >One drawback that I found was the syntax for event writing, which in >dem.lisp is like this: > >(define-event name (args) > (paths) > ((action name < action body) > (action name < action body) > (action name < action body) > (action name < action body) ...) > ) > >There was some question in my mind about whether it would make sense >to define a lot of little events, or one big one. It seemed that the >coordination between little ones, for my purposes, would be >problematic, so I did one big one. Very interesting. Mira Mezini and I have been working on what we call APPCs (adaptive plug-and-play components) which allow you to decompose the behavior into smaller behaviors ("events" in your terminology) and still allow communication between those smaller behaviors. See: http://www.ccs.neu.edu/research/demeter/biblio/components.html I can see that if you have one big event, it can be odd to have to define all actions in one place. Maybe we should rewrite your example in terms of APPCs to test the concept. Is your code proprietary? >The above syntax makes this a >little odd, since I have to define all of my actions in one place, and >with a syntax that my code support tools don't recognize (making >everything indented way to the right). Not a big problem, but >noticeable. Also, if I made a small modification to one action, I >had to regenerate the whole trace (even though no change was made to >the class definitions) and redefine all the methods. For someone who >is used to Lisp, where you can just redefine the smallest chunks and >carry on from where you were, this is a big nuisance. I suppose that >if I were clever, I would have define-event cache its paths, so that >it would only regenerate them if the class definitions or the path >specs changed. If I find myself with spare time and feel a need to >work on demeter, I might do that. > Regeneration is also an issue in Demeter/Java and Doug Orleans has been using Josh Marshall's weaver to alleviate regeneration. Doug, what is the current status of seperate generation in Demeter/Java? > > >Finally, I found that I would like to reference results from one >action in another. In particular, consider the path we talked about >above: > >A B C D E F G C D E X > >The visit to A created some corresponding object in ZD. Then the >visit to, say, E, creates another, which needs to be linked with its >parent. That is, I would like there to be variables that are local to >the event (and available across actions), which scope according to the >path; that is, if there are several Bs, each of which refers to >several Ds, then for each D, a reference to the variable will >correspond to how it was set for *the appropriate B* which is a parent >of that D. > >It turns out that the way lisp handles function arguments makes this >dead easy in dem.lisp. If you declare something as an argument to the >event, then every generated method will have it in its argument list. > >(define-event zorch (x y) > (path) ((A action1) (B action2) ...)) > > >yields > >(defmethod zorch ((a A) x y) ... (zorch B x y) ...) > >(defmethod zorch ((b B) x y) ... (zorch C x y) ...) > >(defmethod zorch ((c C) x y) ... (zorch D x y) ...) > >(defmethod zorch ((d D) x y) ...) > > >This means that if you (setf x ...) in (say) the method for B, then it >will be changed in the call to (zorch C x y), and the method for C can >reference it. When zorch is called on another B, the new calls for C >get the new values of x and y. Since lisp calls functions by value >only, this can never cause any problems beyond where it is supposed to >be. > Communication across classes is very important. In Demeter/Java this is possible through instance variables in visitors. Also APPCs have local instance variables. In Demeter/C++ we did it through arguments in a similar way as you describe above. > > >I have not yet tried to create another translator from Acme to ZD >(which would demonstrate the flexibility of the approach). There are >a couple of obstacles that Demeter will not be able to help me >surmount. > >First, the calls to Acme are done through Corba, and hence are very >expensive. This means that I need to cache values, so I wrote (in >Lisp) a layer between Corba and Demeter. This would have to be >re-written if we were to change the Acme style. > >The way dem.lisp is written, it creates the (lisp) classes when the >demeter classes are defined. Since the Orb creates classes too, even >if I didn't do the cache, it would be tricky to avoid having an >intermediary class. > >Many of the "links" from one acme class to another are not done along >simply slot calls, but are done with search calls; for example, I have >something called an "effect" that certain things can have. "effect" >is not a slot of that class in Acme; instead, it is a particular port >with the name "effect". It can be retrieved using a call like > > (acme:getport requirement "effect") > >dem.lisp is not set up to create calls of this sort. However, in the >cache layer, I can encapsulate these things, so that the call > > (effect requirement) > >does this call. dem.lisp has no trouble creating a call like this. > > > >All in all, the experience was a positive one. It seemed time >consuming because of the cache layer that I had to write, but this >would have had to be written in any case, even if I had written the >translator from scratch without demeter. I have written a number of >translators of this sort, and I have to say that the control issues in >this one were much easier to handle; I often find myself wondering >"where do I start?" when writing one of these things, and spend a lot >of time coding the tree walk (remembering which points are single >references, which ones are lists, etc.). The power of the "by hand" >method is that idiosyncratic calls (like the > >(getport requirement "effect") > >above) are no harder than anything else; the problem is that >everything is just as hard (in the by-hand method). > >So, there is my experience report - you can share this with your >students, if you think it is appropriate. > >Dean > > We are going to add a JavaBeans-aspect to Demeter/Java to make it easy to interoperate with other JavaBeans. Based on your description above this aspect should take caching into account. Is it possible to get a hold of your Lisp code to have another concrete playing ground for APPCs etc? Glad to know that a traversal approach works also well in Common Lisp. Best regards, -- Karl PS. This is referenced in http://www.ccs.neu.edu/home/lieber/outside-impl.html