Dynamically Scoped Functions As The Essence of AOP
Dynamically Scoped Functions As The Essence of AOP
Pascal Costanza
University of Bonn, Institute of Computer Science III
Römerstr. 164, D-53117 Bonn, Germany
[email protected], http://www.pascalcostanza.de
June 17, 2003
Table 1: A dynamically scoped redefinition of the fibonacci function that caches computations.
If a dflet attempts to define a function that is not A dynamically scoped definition of fib allows us to
properly prepared, an error handler allows the program- cache the computations – see Table 1 above.
mer to correct this situation (lines 32-37). If redefdynfun The technique used here is essentially the same as that
is applied to a function name that does not exist yet, of [17]: Results are looked up in an association list. When
a function with that name is prepared for dynamically they are found, they are immediately returned; otherwise
scoped definitions nonetheless, which in turn signals an the original function is called, the result is stored in the
error on invocation as its default behavior (and thus be- association list and finally returned. All this is inter-
haves like any other undefined function; see lines 20-24). spersed with verbose output. The body of dflet is a
The dflet1 helper macro allows for the dynamically loop that allows interactive invocations of fib and obser-
scoped definition of exactly one function, whereas the vation of the caching behavior.
dflet macro allows for zero or more definitions and uses Here is a transcript of an example session:
dflet1 repeatedly.
(Common Lisp’s (reduce ... :from-end t) is better > (fib 4)
known to the world as foldr.) called (fib 1) => 1
The ensure-dynsym and dflet1 macros make use called (fib 0) => 1
of with-gensyms as described in the chapter on clas- called (fib 2) => 2
sic macros in [9]. Apart from that, the source code found (fib 1) => 1
uses only forms as defined by ANSI Common Lisp called (fib 3) => 3
[1, 18]. The code has been developed and tested with found (fib 2) => 2
Macintosh Common Lisp 5.0 (http://www.digitool.com) called (fib 4) => 5
and the beta version of LispWorks for Macintosh 4.3
(http://www.lispworks.com). 5
> (fib 4)
2.2 An Example found (fib 4) => 5
Following the tradition to illustrate aspect-oriented pro-
5
gramming with caching of fibonacci numbers, we show
how to implement this in our approach. First we give
a function definiton for fibonacci numbers, CLOS-style, 3 Related Work
and declare it to be overridable by dflet:
3.1 Dynamic Scope
; methods for the "objects" 0 and 1
(defmethod fib ((x (eql 0))) 1) Scheme does not provide any standard constructs for dy-
(defmethod fib ((x (eql 1))) 1) namic scoping - in [21], an implementation of dynamically
scoped variables on top of lexical scoping is presented,
; a method for all other cases and the reports that define the Scheme standard head
(defmethod fib (x) for minimalism and conceptual simplicity instead of com-
(+ (fib (- x 1)) pleteness.
(fib (- x 2)))) However, many Scheme dialects, for example Mz-
Scheme [6], provide dynamic scope in two ways: as the
(redefdynfun fib) fluid-let form, and as parameter objects. For example,
the idea presented in this paper can be implemented with 3.2 Aspect-Oriented Programming
fluid-let roughly as follows:
There have been previous aspect-oriented attempts at
providing language constructs for rebinding / amending
(define (f x) (print x)) existing functions at runtime, similar to our approach
based on dynamically scoped functions. In [14], the se-
(let ((call-next-function f)) mantics of a superimposition language construct are de-
(fluid-let ((f (lambda (x) fined that allows for method call interception (MCI). Like
(print "entering f") in our approach, method call interceptors can be acti-
(call-next-function x) vated and deactivated at arbitrary points in the control
(print "leaving f")))) flow of a program and have dynamic extent. However,
(f 5))) that paper defines a superimposed method to globally
replace the method’s original definition during that ex-
tent. This would have the same drawbacks as MzScheme’s
A major drawback of fluid-let is that it is explicitly fluid-let in the presence of multi-threading, but can
defined to save a copy of the previous value of a global probably be corrected by switching to one of the thread-
variable on the stack and establish the new binding by safe techniques. Approaches like Handi-Wrap [2] and As-
side-effecting that variable. This implementation breaks pectS [11] can be regarded as implementations of the se-
in the case of multi-threading - each thread should have mantics described in [14], and they have the same draw-
their own independent bindings for dynamically scoped backs and differences to our approach. In [15], an ap-
variables instead of randomly modifying shared storage. proach is described how to add superimposition of this
In fact, Common Lisp implementations that incorporate kind to any language, provided the semantics of a lan-
multi-threading typically treat special variables as intu- guage are given in a suitable style.
itively expected.
In [24], an aspect-oriented extension of Scheme is de-
This is also true for parameter objects in MzScheme: a scribed that provides both constructs for static and dy-
parameter object can be accessed as a function that either namic weaving (around and fluid-around). In that ap-
gets or sets its current value, and it can be given a new proach, fluid-around stores and looks up aspect activa-
binding with dynamic scope in a thread-safe manner by tions in the call stack, and therefore is thread-safe. The
way of the parameterize form. However, the fact that static around construct records aspects in a global en-
parameter objects can only be accessed via functions to- vironment and ensures that closures keep their respec-
gether with Scheme’s preference of macro hygiene requires tive static aspects when they escape the scope of an
a little bit more gymnastics for an implementation of our around activation. The same degree of “stickiness” can
approach. be achieved in our approach by passing around the clo-
Recent attempts at introducing dynamically scoped sure stored in the symbol returned by get-dynsym (line
variables into C++ [10] and Haskell [16] would in princi- 3 in Appendix A), instead of the function itself.
ple also be suitable for our approach because they are also All the dynamic aspect-oriented approaches described
implemented by passing dynamic environments to func- above can be regarded as derivations of the idea to run
tions behind the scenes, instead of modifying global state. programs inside a specialized “aspect-oriented” monad
However, we have not yet worked out the details in either [17]. However, the former approaches are embedded in
case. the respective base languages while the latter requires all
Emacs Lisp comes with a Common Lisp compatibility interceptors to be defined in the monad. The fundamen-
package that differs from ANSI Common Lisp for example tal difference to our approach is that all those approaches
with regard to local functions: flet is lexically scoped still provide what are essentially reflective metaprogram-
in ANSI Common Lisp, but dynamically scoped in that ming constructs, whereas our approach is located exclu-
package [7]. This means that an implementation of our sively at the base level of the programming language and
approach would be straightforward in Emacs Lisp. is based on the considerably simpler view that aspects are
It is interesting to note that many programming lan- just local redefinitions of functions with dynamic extent.
guages provide dynamically scoped functions in the form Aspect-Oriented Programming has been characterized
of exception handlers: a throw of an exception can be as a combination of quantification and obliviousness [5]
understood as a call to the dynamically scoped excep- (see also [4]). Obliviousness means that aspects can de-
tion handler associated with that exception. However, fine certain, typically non-functional, properties of target
exceptions are not especially useful for aspect-oriented code without the need for the target code to have an ex-
programming because throws of exceptions are too ex- plicit awareness of those aspects. Dynamic scope for func-
plicit. tion definitions captures this obliviousness dimension of
AOP very well: code that uses certain functions can run Acknowledgements The author thanks the follow-
in contexts with or without varying dynamically scoped ing people for inspiration, comments and fruitful discus-
redefinitions of those functions. The quantification di- sions, in alphabetical order: Eli Barzilay, Tim Bradshaw,
mension is not directly covered by dynamically scoped Thomas F. Burdick, Jeff Caldwell, Paul Foley, Erann Gat,
functions. Quantification means that an aspect defini- Steven M. Haflich, Robert Hirschfeld, Kaz Kylheku, Ralf
tion can be applied to more than one place at a time, and Lämmel, Barry Margolin, Joe Marshall, Wolfgang De
is available for example in AspectJ by means of the point- Meuter, Mario S. Mommer, Pekka P. Pirinen, Kent M.
cut construct that can span a multitude of join points in Pitman, Alexander Schmolck, Nikodemus Siivola. Spe-
the target code. However in Lisp dialects, quantification cial thanks to Xanalys for allowing me to use the beta
is already adequately captured as one of the many uses version of LispWorks for Macintosh to perform some ad-
of structural macros. As an illustration of this point we ditional tests.
give a sketch of a multidflet macro:
(defmacro multidflet ((functions &body def) References
&body body)
‘(dflet ,(mapcar [1] ANSI/INCITS X3.226-1994. American National
(lambda (function) Standard for Information Systems - Programming
‘(,function (&rest args) ,@def)) Language - Common Lisp, 1994. See [18] for an on-
functions) line version of this document.
,@body))
[2] J. Baker and W. Hsieh. Runtime Aspect Weaving
Its effect is that the single function definiton def is through Metaprogramming. In: AOSD 2002 - Pro-
used for the dynamically scoped redefinitions of the list of ceedings. ACM Press, 2002.
function symbols functions. The elaboration of possible
usage scenarios is left as an exercise to the reader. [3] T. Bradshaw. Maintaining dynamic state, 2001.
http://www.tfeb.org/lisp/hax.html#DYNAMIC-
STATE
4 Conclusions [4] R. Filman. What Is Aspect-Oriented Programming,
This paper is a slightly extended version of a submission Revisited. Workshop on Advanced Separation of
accepted for the workshop on “object-oriented language Concerns, ECOOP 2001, Budapest, Hungary.
engineering for the post-Java era” at ECOOP 2003.2 [5] R. Filman and D. Friedman. Aspect-Oriented
Hopefully, it reveals the following points, among others: Programming is Quantification and Obliviousness.
• Programming languages that support a tendency to Workshop on Advanced Separation of Concerns,
focus on static properties of programs can be a hin- OOPSLA 2000, Minneapolis, USA.
drance to see the forest from the trees. The aspect-
[6] M. Flatt. PLT MzScheme: Language Manual, 2002.
oriented programming community devotes lots of en-
http://download.plt-scheme.org/doc/
ergy into the provision of complex static means to
reason about eventual dynamic properties of a pro- [7] D. Gillespie. Common Lisp Extensions. Included in
gram. Only the focus on a strongly dynamically- distributions of GNU Emacs and XEmacs.
natured language construct reveals that we are in http://www.gnu.org/software/emacs/emacs.html
fact dealing with a very simple mechanism. and http://www.xemacs.org/
• This mechanism is that of dynamic scoping for [8] J. Gosling, B. Joy, G. Steele, and G. Bracha.
functions. Only the addition of a structured way The Java Language Specification, Second Edition.
to call the previous definition of a function, via Addison-Wesley, 2000.
call-next-function, is needed to turn it into a full-
fledged aspect-oriented approach. [9] P. Graham. On Lisp. Prentice-Hall, 1993.
http://www.paulgraham.com/onlisp.html
• Appendix A gives a complete executable implemen-
tation of the ideas presented in this paper, but the [10] D. Hanson and T. Proebsting. Dynamic Variables.
paper still respects the page limit as imposed both In: PLDI 2001 - Proceedings. ACM Press, 2001.
by that workshop and by SIGPLAN Notices. This [11] R. Hirschfeld. AspectS - Aspect-Oriented Program-
is only possible because a sufficiently complete and ming with Squeak. In: Objects, Components, Archi-
well-balanced language is used. tectures, Services, and Applications for a Networked
2 http://prog.vub.ac.be/∼wdmeuter/PostJava/ World. Springer LNCS 2591, 2003.
[12] R. Kelsey, W. Clinger, J. Rees (eds.). Revised5 Re- [18] K. Pitman (ed.). Common Lisp HyperSpec, 2001.
port on the Algorithmic Language Scheme. Higher- http://www.lispworks.com/reference/HyperSpec/
Order and Symbolic Computation, Vol. 11, No. 1,
September, 1998 and ACM SIGPLAN Notices, Vol. [19] G. Steele. Common Lisp the Language, 2nd Edi-
33, No. 9, October, 1998. tion. Digital Press, 1990. http://www-2.cs.cmu.edu
/Groups/AI/html/cltl/cltl2.html
[13] G. Kiczales, E. Hilsdale, J. Hugunin, M. Kersten, J.
Palm, W. Griswold. An Overview of AspectJ. In: [20] G. Steele and R. Gabriel. The Evolution of Lisp.
ECOOP 2001 - Proceedings. Springer LNCS 2027, In: T. Bergin, R. Gibson. History of Programming
2001. Languages II. Addison-Wesley, 1996.
[14] R. Lämmel. A Semantical Approach to Method Call [21] G. Steele and G. Sussman. Lambda - The Ultimate
Interception. In: AOSD 2002 - Proceedings. ACM Imperative. MIT AI Lab, AI Lab Memo AIM-353,
Press, 2002. March 1976.
[15] R. Lämmel. Adding Superimposition To a Language [22] G. Steele and G. Sussman. The Revised Report on
Semantics. In: FOAL 2002 Proceedings: Foun- Scheme - A Dialect of Lisp. MIT AI Lab, AI Lab
dations of Aspect-Oriented Languages Workshop at Memo AIM-452, January 1978.
AOSD 2002. Technical Report, Department of Com- [23] G. Steele and G. Sussmann. The Art of the Inter-
puter Science, Iowa State University, 2003. preter or, the Modularity Complex (Parts Zero, One,
[16] J. Lewis, J. Launchbury, E. Meijer, M. Shields. and Two). MIT AI Lab, AI Lab Memo AIM-453,
Implicit Parameters: Dynamic Scoping with Static May 1978.
Types. In: POPL 2000 - Proceedings. ACM Press, [24] D. Tucker and S. Krishnamurthi. Pointcuts and Ad-
2000. vice in Higher-Order Languages. In: AOSD 2003 -
[17] W. De Meuter. Monads as a theoretical foundation Proceedings. ACM Press, 2003.
for AOP. In: International Workshop on Aspect-
Oriented Programming at ECOOP, 1997.
A The complete source code for dflet
1: (defvar *dynsyms* (make-hash-table :test #’equal))
2:
3: (defmacro get-dynsym (fname) ‘(gethash ,fname *dynsyms*))
4:
5: (defun make-dynsym (fname)
6: (setf (get-dynsym fname) (make-symbol (format nil "*~A*" fname))))
7:
8: (defmacro ensure-dynsym (fname default)
9: (with-gensyms (sym) ‘(let ((,sym (get-dynsym ,fname)))
10: (if ,sym ,sym ,default))))
11:
12: (defun ensure-dynfun-form (fname &rest rest)
13: (let ((dynsym (ensure-dynsym fname (make-dynsym fname))))
14: ‘(progn (setf (get-dynsym ’,fname) ’,dynsym)
15: (defparameter ,dynsym
16: ,(if rest
17: ‘(lambda ,@rest)
18: ‘(if (fboundp ’,fname)
19: (fdefinition ’,fname)
20: (lambda (&rest args)
21: (cerror "Retry applying ~A to ~A."
22: "Undefined dynamic function ~A called with arguments ~A."
23: ’,fname args)
24: (apply ’,fname args)))))
25: (defun ,fname (&rest args) (apply ,dynsym args)))))
26:
27: (defmacro defdynfun (fname args &body body)
28: (apply #’ensure-dynfun-form fname args body))
29:
30: (defmacro redefdynfun (fname) (ensure-dynfun-form fname))
31:
32: (defun get-defined-dynsym (fname)
33: (ensure-dynsym fname (progn (cerror "Make ~A a dynamic function."
34: "Function ~A is not dynamic."
35: fname)
36: (eval ‘(redefdynfun ,fname))
37: (get-dynsym fname))))
38:
39: (defmacro dflet1 ((fname (&rest args) &body funbody) &body dflbody)
40: (let ((dynsym (get-defined-dynsym fname)))
41: (with-gensyms (orgfun orgargs newargs)
42: ‘(let* ((,orgfun ,dynsym)
43: (,dynsym (lambda (&rest ,orgargs)
44: (flet ((call-next-function (&rest ,newargs)
45: (apply ,orgfun (if ,newargs ,newargs ,orgargs))))
46: (destructuring-bind ,args ,orgargs ,@funbody)))))
47: (declare (ignorable ,orgfun))
48: ,@dflbody))))
49:
50: (defmacro dflet ((&rest decls) &body body)
51: (reduce (lambda (decl result) ‘(dflet1 ,decl ,result)) decls
52: :initial-value ‘(progn ,@body) :from-end t))