Macros for seriously interested people



Here are a couple of links that demonstrate good uses of macros in
Common Lisp. These examples are not short, nor are they particularly
macro-laden. They show how a couple of judiciously placed macros can
make useful changes to the language.

Pragmatic Parsing in Common Lisp
Henry Baker
http://home.pipeline.com/~hbaker1/Prag-Parse.html

SERIES package for Common Lisp
Richard Waters
http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node347.html

@techreport{ siskind91screamer,
author = "Jeffrey Mark Siskind and David Allen McAllester",
title = "Screamer: {A} Portable Efficient Implementation of
Nondeterministic Common Lisp",
number = "IRCS-93-03",
address = "Philadelphia, PA",
year = "1991",
url = "citeseer.ist.psu.edu/siskind93screamer.html" }

Funny, but I seem to have argued this question before with Alex
Martelli.
http://mail.python.org/pipermail/python-list/2003-October/229805.html

I gave two examples of interesting macro expansion. One from series:

(defun example ()
(let ((elements '(3 -1 4 -1 5 -9 2 -6 5 -3)))
(/ (reduce #'max (map 'list #'abs elements))
(reduce #'+ elements))))

This works, but it is inefficient because MAP and the second REDUCE
both traverse the list, and the intermediate list generated by MAP is
immediately discarded (consumed) by the first REDUCE.

The series version appears quite similar:

(defun series-example ()
(let ((elements (scan '(3 -1 4 -1 5 -9 2 -6 5 -3))))
(/ (collect-max (#m abs elements))
(collect-sum elements))))

but if you macroexpand the body, you get this:

(COMMON-LISP:LET* (ELEMENTS (#:LISTPTR-702 '(3 -1 4 -1 5 -9 2 -6 5
-3)) #:ITEMS-710 (#:NUMBER-707 NIL) (#:SUM-714 0))
(DECLARE (TYPE LIST #:LISTPTR-702) (TYPE NUMBER #:SUM-714))
(TAGBODY
#:LL-717 (IF (ENDP #:LISTPTR-702) (GO SERIES::END))
(SETQ ELEMENTS (CAR #:LISTPTR-702))
(SETQ #:LISTPTR-702 (CDR #:LISTPTR-702))
(SETQ #:ITEMS-710 ((LAMBDA (#:V-708) (ABS #:V-708))
ELEMENTS))
(IF (OR (NULL #:NUMBER-707) (< #:NUMBER-707
#:ITEMS-710)) (SETQ #:NUMBER-707 #:ITEMS-710))
(SETQ #:SUM-714 (+ #:SUM-714 ELEMENTS))
(GO #:LL-717)
SERIES::END)
(IF (NULL #:NUMBER-707) (SETQ #:NUMBER-707 NIL))
(/ #:NUMBER-707 #:SUM-714))

This code avoids traversing the series more than once and does not
create an intermediate series just to discard it. It does this by
analyzing the code and determining that the computation may procede in
`lock step'.

This example is from Screamer:
(and seems relevant to a different thread on this list!)

(defun pythagorean-triples (n)
(all-values
(let ((a (an-integer-between 1 n))
(b (an-integer-between 1 n))
(c (an-integer-between 1 n)))
(unless (= (+ (* a a) (* b b)) (* c c)) (fail))
(list a b c))))

The body of this function expands into:

(LET ((VALUES 'NIL) (SCREAMER::LAST-VALUE-CONS NIL))
(LET ((SCREAMER::TRAIL-POINTER (FILL-POINTER SCREAMER::*TRAIL*)))
(SCREAMER::CHOICE-POINT-INTERNAL
(PROGN
(LET ((#:DUMMY-29301 N))
(LET ((#:CONTINUATION-29303
#'(LAMBDA (&OPTIONAL #:DUMMY-29283 &REST
#:OTHER-29284)
(DECLARE (SCREAMER::MAGIC) (IGNORE
#:OTHER-29284))
(PROGN
(LET ((#:DUMMY-29296 N))
(LET ((#:CONTINUATION-29298
#'(LAMBDA (&OPTIONAL #:DUMMY-29285
&REST #:OTHER-29286)
(DECLARE (SCREAMER::MAGIC)
(IGNORE #:OTHER-29286))
(PROGN
(LET ((#:DUMMY-29291 N))
(LET ((#:CONTINUATION-29293
#'(LAMBDA (&OPTIONAL
#:DUMMY-29287 &REST #:OTHER-29288)
(DECLARE
(SCREAMER::MAGIC) (IGNORE #:OTHER-29288))
(LET ((C
#:DUMMY-29287)
(B
#:DUMMY-29285)
(A
#:DUMMY-29283))
(PROGN
(IF (NULL (=
(+ (* A A) (* B B)) (* C C)))
(PROGN
(FAIL)))
(LET
((#:DUMMY-29281 (LIST A B C)))
(LET
((SCREAMER::VALUE #:DUMMY-29281))
(PROGN

(GLOBAL

(COND ((NULL VALUES)

(SETF

SCREAMER::LAST-VALUE-CONS

(LIST SCREAMER::VALUE))

(SETF

VALUES

SCREAMER::LAST-VALUE-CONS))

(T

(SETF

(REST SCREAMER::LAST-VALUE-CONS)

(LIST SCREAMER::VALUE))

(SETF

SCREAMER::LAST-VALUE-CONS

(REST

SCREAMER::LAST-VALUE-CONS)))))

(FAIL)))))))))
(DECLARE (DYNAMIC-EXTENT
#:CONTINUATION-29293))
(SCREAMER::AN-INTEGER-
BETWEEN-NONDETERMINISTIC #:CONTINUATION-29293

1

#:DUMMY-29291)))))))
(DECLARE (DYNAMIC-EXTENT
#:CONTINUATION-29298))
(SCREAMER::AN-INTEGER-BETWEEN-
NONDETERMINISTIC #:CONTINUATION-29298

1

#:DUMMY-29296)))))))
(DECLARE (DYNAMIC-EXTENT #:CONTINUATION-29303))
(SCREAMER::AN-INTEGER-BETWEEN-NONDETERMINISTIC
#:CONTINUATION-29303 1 #:DUMMY-29301))))))
VALUES)

.



Relevant Pages

  • Re: Macros for seriously interested people
    ... Pragmatic Parsing in Common Lisp ... (defun series-example () ... (PROGN ... (SETF ...
    (comp.lang.lisp)
  • Re: Assistance Needed: Setf
    ... If setf is a macro, why do I get this in LispWorks: ... makes sense to talk about operator overloading in Lisp. ... for example the very generic compiler macros. ... Since Common Lisp is a dynamically typed ...
    (comp.lang.lisp)
  • Re: Assistance Needed: Setf
    ... left-hand side constructs for 'setf, ... The other Pascal's comment is correct, but let me add that just because setf is somewhat similar to operator overloading in C++ doesn't mean that operator overloading would be a good idea for Lisp, or that it even makes sense to talk about operator overloading in Lisp. ... Common Lisp offers other user-extensible macros based on statically available information, like for example the very generic compiler macros. ...
    (comp.lang.lisp)
  • Re: Request for help constructing a simple macro
    ... (setf b (pop list)) ... `(progn,@(loop for place in places-to-set ... I *always* had a lot of trouble writing my own macros. ...
    (comp.lang.lisp)
  • Re: ILC2005: McCarthy denounces Common Lisp, "Lisp", XML, and Rahul
    ... The examples I'm thinking of were DW to CLIM, ... > code) to Common Lisp in about 3 months. ... > function call parts, not the macros. ... > if it's part of the design strategy. ...
    (comp.lang.lisp)