Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!lll-crg!rutgers!pyrnj!mirror!cca!lmi-angel!rpk From: rpk@lmi-angel.UUCP (Bob Krajewski) Newsgroups: comp.lang.lisp Subject: Re: A small brush fire ... Message-ID: <99@lmi-angel.UUCP> Date: Sun, 23-Nov-86 17:08:59 EST Article-I.D.: lmi-ange.99 Posted: Sun Nov 23 17:08:59 1986 Date-Received: Mon, 24-Nov-86 22:06:21 EST Reply-To: rpk@lmi-angel.UUCP (Bob Krajewski) Organization: LISP Machine, Inc (Cambridge Engineering HQ) Lines: 102 Keywords: scoping, style, Zetalisp, Common Lisp, macros, wrappers Summary: Scoping DOES matter In article <> jds@duke.UUCP (Joseph D. Sloan) writes: > So how would you answer the following: Since "good" >programmers know better than using free (== global) variables, >the argument between lexical and dynamic scoping is really a >mute point. The real issue this: Can you exhibit a piece of code >that depends on the scoping rules that can't be coded AS WELL >in a way that doesn't depend on the scoping rules? > With lexical scoping, it's a lot easier to use local functions that refer to variables in the enclosing binding construct, where *those* variables are also intended to be local to the enclosing function. In other words, in lexically scoped Common Lisp we can write: (defun map-increment (list n) (mapcar #'(lambda (elt) (+ elt n)) list)) where the anonymous function makes a reference to N that is lexical, but N is bound by MAP-INCREMENT. In a MACLISP-style Lisp, where lexical scoping is foiled by lambda-expressions (i.e., local variables were only the ones that could be referred to by going up binding contours until you hit the establishment of a function), there are a few ways around this: 1. Handling situations that would want to use ``real'' lexical scoping specially. For the MAP family of functions, this could be handled by open-coding the loop, and transforming ((lambda () ) ) into (let (( ) ( ) ...) ) This strategy supported what people were likely to do with MAPCAR and friends. But if you had your own function-using function, and you were not a system implementor, you were out of luck and had to use one of the following strategies (or, if you are less charitable, kludges). 2. Declaring the needed variables globally special. (defun frobnicate-list (function list) (cons 'frobnicated-list (mapcar function list))) (defvar *frob*) ; No top-level value (defun frob-list (list *frob*) (frobnicate-list #'(lambda (elt) (frob-element elt *frob*)) list)) 3. Declaring the needed variables locally specially as needed. (local-declare ((special frob)) ; special form for local declarations (defun frobnicate-list (list frob) (frobnicate-list #'(lambda (elt) (frob-element elt frob)) list)) ) In 2 and 3, the dynamic scope of the extra variable will allow the lambda-expression to see the right value. In either of these cases, the programmer has to go out of his way to get around the inadequacies of such lazy scoping rules (which were different in the compiler and the interpreter). One nice technique that lexical scoping allows you is a functional way of implementing macros that wrap code. Basically, a handler function funcalls a local function (maybe with some other arguments), instead of open-coding what the macro is supposed to do. This technique can't work unless locally generated functions can access any lexically available variables with special declarations, fuss, or bother. Here is a simple example of the technique. The open-coding style would look like: (defmacro without-mangoes (&body body) `(unwind-protect (progn (shut-off-mangoes) ,@body) (turn-on-mangoes))) The functional style would look like: (defmacro without-mangoes (&body body) `(without-mangoes-1 #'(lambda () ,@body))) ;;; Here is the run-time support function (defun without-mangoes-1 (f) (unwind-protect (progn (shut-off-mangoes) (funcall f)) (turn-off-mangoes))) This is a good strategy when implementing a macro that has an elaborate wrapper which, if implemented in the straightforward way, would increase the size of the function that uses it by making references to more constants, generating local variables, etc. Also, only the support function has to be recompiled to effect a change, instead of recompiling all uses of the macro. Since the strategy will involve at least two additional function calls at run time, and cause references to local variables not immediately inside the current frame, it is less efficient, but probably not noticeably less so if used away from loop code. -- Robert P. Krajewski Internet/MIT: RPK@MC.LCS.MIT.EDU UUCP: ...{cca,harvard,mit-eddie}!lmi-angel!rpk