DOC PREVIEW
UMBC CMSC 331 - Classic Macros

This preview shows page 1-2-21-22 out of 22 pages.

Save
View full document
View full document
Premium Document
Do you want full access? Go Premium and unlock all 22 pages.
Access to all documents
Download any document
Ad free experience
View full document
Premium Document
Do you want full access? Go Premium and unlock all 22 pages.
Access to all documents
Download any document
Ad free experience
View full document
Premium Document
Do you want full access? Go Premium and unlock all 22 pages.
Access to all documents
Download any document
Ad free experience
View full document
Premium Document
Do you want full access? Go Premium and unlock all 22 pages.
Access to all documents
Download any document
Ad free experience
Premium Document
Do you want full access? Go Premium and unlock all 22 pages.
Access to all documents
Download any document
Ad free experience

Unformatted text preview:

11Classic MacrosThis chapter shows how to define the most commonly used types of macros.They fall into three categories—with a fair amount of overlap. The first groupare macros which create context. Any operator which causes its arguments tobe evaluated in a new context will probably have to be defined as a macro. Thefirst two sections describe the two basic types of context, and show how to definemacros for each.The next three sections describe macros for conditional and repeated evalua-tion. An operator whose arguments are to be evaluated less than once, or morethan once, must also be defined as a macro. There is no sharp distinction betweenoperators for conditional and repeated evaluation: some of the examples in thischapter do both (as well as binding). The final section explains another similaritybetween conditional and repeated evaluation: in some cases, both can be donewith functions.11.1 Creating ContextContext here has two senses. One sort of context is a lexical environment. Thelet special form creates a new lexical environment; the expressions in the bodyof a let will be evaluated in an environment which may contain new variables.If x is set to a at the toplevel, then(let ((x ’b)) (list x))will nonetheless return (b), because the call to list will be made in an environ-ment containing a new x, whose value is b.143144 CLASSIC MACROS(defmacro our-let (binds &body body)‘((lambda ,(mapcar #’(lambda (x)(if (consp x) (car x) x))binds),@body),@(mapcar #’(lambda (x)(if (consp x) (cadr x) nil))binds)))Figure 11.1: Macro implementation of let.An operator which is to have a body of expressions must usually be defined asa macro. Except for cases like prog1 and progn, the purpose of such an operatorwill usually be to cause the body to be evaluated in some new context. A macrowill be needed to wrap context-creating code around the body, even if the contextdoes not include new lexical variables.Figure 11.1 shows how let could be defined as a macro on lambda.Anour-let expands into a function application—(our-let ((x 1) (y 2))(+ x y))expands into((lambda (x y) (+ x y)) 1 2)Figure 11.2 contains three new macros which establish lexical environments.Section 7.5 used when-bind as an exampleof parameter list destructuring, so thismacro has already been described on page 94. The more general when-bind*takes a list of pairs of the form (symbol expression)—the same form as thefirst argument to let.Ifanyexpression returns nil, the whole when-bind*expression returns nil. Otherwise its body will be evaluated with each symbolbound as if by let*:> (when-bind* ((x (find-if #’consp ’(a (1 2) b)))(y (find-if #’oddp x)))(+ y 10))11Finally, the macro with-gensyms is itself for use in writing macros. Manymacro definitions begin with the creation of gensyms, sometimes quite a numberof them. The macro with-redraw (page 115) had to create five:11.1 CREATING CONTEXT 145(defmacro when-bind ((var expr) &body body)‘(let ((,var ,expr))(when ,var,@body)))(defmacro when-bind* (binds &body body)(if (null binds)‘(progn ,@body)‘(let (,(car binds))(if ,(caar binds)(when-bind* ,(cdr binds) ,@body)))))(defmacro with-gensyms (syms &body body)‘(let ,(mapcar #’(lambda (s)‘(,s (gensym)))syms),@body))Figure 11.2: Macros which bind variables.(defmacro with-redraw ((var objs) &body body)(let ((gob (gensym))(x0 (gensym)) (y0 (gensym))(x1 (gensym)) (y1 (gensym)))...))Such definitions are simplified by with-gensyms, which binds a whole list ofvariables to gensyms. With the new macro we would write just:(defmacro with-redraw ((var objs) &body body)(with-gensyms (gob x0 y0 x1 y1)...))This new macro will be used throughout the remaining chapters.If we want to bind some variables and then, depending on some condition,evaluate one of a set of expressions, we just use a conditional within a let:(let ((sun-place ’park) (rain-place ’library))(if (sunny)(visit sun-place)(visit rain-place)))146 CLASSIC MACROS(defmacro condlet (clauses &body body)(let ((bodfn (gensym))(vars (mapcar #’(lambda (v) (cons v (gensym)))(remove-duplicates(mapcar #’car(mappend #’cdr clauses))))))‘(labels ((,bodfn ,(mapcar #’car vars),@body))(cond ,@(mapcar #’(lambda (cl)(condlet-clause vars cl bodfn))clauses)))))(defun condlet-clause (vars cl bodfn)‘(,(car cl) (let ,(mapcar #’cdr vars)(let ,(condlet-binds vars cl)(,bodfn ,@(mapcar #’cdr vars))))))(defun condlet-binds (vars cl)(mapcar #’(lambda (bindform)(if (consp bindform)(cons (cdr (assoc (car bindform) vars))(cdr bindform))))(cdr cl)))Figure 11.3: Combination of cond and let.Unfortunately, there is no convenient idiom for the opposite situation, wherewe always want to evaluate the same code, but where the bindings must varydepending on some condition.Figure 11.3 contains a macro intended for such situations. As its namesuggests, condlet behaves like the offspring of cond and let. It takes asarguments a list of binding clauses, followed by a body of code. Each of thebinding clauses is guarded by a test expression; the body of code will be evaluatedwith thebindingsspecifiedby the first bindingclause whose test expressionreturnstrue. Variables which occur in some clauses and not others will be bound to nilif the successful clause does not specify bindings for them:11.2 THE with- MACRO 147> (condlet (((= 1 2) (x (princ ’a)) (y (princ ’b)))((= 1 1) (y (princ ’c)) (x (princ ’d)))(t (x (princ ’e)) (z (princ ’f))))(list x y z))CD(D C NIL)The definition of condlet can be understood as a generalization of the def-inition of our-let. The latter makes its body into a function, which is appliedto the results of evaluating the initial-value forms. A condlet expands into codewhich defines a local function with labels; within it a cond clause determineswhich set of initial-value forms will be evaluated and passed to the function.Notice that the expander uses mappend instead of mapcan to extract thevariable names from the binding clauses. This is because mapcan is destructive,and as Section 10.3 warned, it is dangerous to modify parameter list structure.11.2 The with- MacroThere is another kind of context besides a lexical environment. In the broadersense, the contextis thestate ofthe world, includingthe valuesof special variables,the contents of data structures, and the state of things outside Lisp. Operatorswhich build this kind of context must be defined as macros too, unless their codebodies are to be packaged up in closures.The names of context-building


View Full Document

UMBC CMSC 331 - Classic Macros

Documents in this Course
Semantics

Semantics

14 pages

Java

Java

12 pages

Java

Java

31 pages

V

V

46 pages

Semantics

Semantics

11 pages

Load more
Download Classic Macros
Our administrator received your request to download this document. We will send you the file to your email shortly.
Loading Unlocking...
Login

Join to view Classic Macros and access 3M+ class-specific study document.

or
We will never post anything without your permission.
Don't have an account?
Sign Up

Join to view Classic Macros 2 2 and access 3M+ class-specific study document.

or

By creating an account you agree to our Privacy Policy and Terms Of Use

Already a member?