PRACTICAL COMMON LISP Peter Seibel 1.

Slides:



Advertisements
Similar presentations
Some non-recursive tricks. The Lambda expression. More on Let, Let*, apply and funcall.
Advertisements

Macro Processor.
Macros and Function Generators CS 480/680 – Comparative Languages.
A problem with functions: arguments must always have values that can be worked out Whenever we call a function we give it arguments. Lisp then works out.
Computer Science 1620 Loops.
VBA Modules, Functions, Variables, and Constants
Operator Overloading in C++ Systems Programming. Systems Programming: Operator Overloading 22   Fundamentals of Operator Overloading   Restrictions.
ITERATIVE CONSTRUCTS: DOLIST Dolist is an iterative construct (a loop statement) consisting of a variable declaration and a body The body states what happens.
CSE S. Tanimoto Macros 1 Defining Macros in Lisp Extensibility: A language is extensible if the language can be extended. New Lisp control structures.
How to load a program file? Lisp programs in Allegro are saved under the file extension.cl. To load a file into the Lisp console, use the following: (load.
Using Templates Object-Oriented Programming Using C++ Second Edition 11.
Functional Programming COMP2003 A course on functional programming using Common Lisp Dr Eleni Mangina
Operator Overloading in C++
COMP 205 – Week 11 Dr. Chunbo Chu. Intro Lisp stands for “LISt Process” Invented by John McCarthy (1958) Simple data structure (atoms and lists) Heavy.
Day 4 Objectives Constructors Wrapper Classes Operators Java Control Statements Practice the language.
While Loops and Do Loops. Suppose you wanted to repeat the same code over and over again? System.out.println(“text”); System.out.println(“text”); System.out.println(“text”);
Fundamentals of Python: From First Programs Through Data Structures
Fundamentals of Python: First Programs
1.  Collections are data structures that holds data in different ways for flexible operations  C# Collection classes are defined as part of the ◦ System.Collections.
by Chris Brown under Prof. Susan Rodger Duke University June 2012
Programming with Alice Computing Institute for K-12 Teachers Summer 2011 Workshop.
PRACTICAL COMMON LISP Peter Seibel 1.
PRACTICAL COMMON LISP Peter Seibel 1.
Common Lisp Macros Read for Input Macros Macro lifetime Macro syntax
Chapter 10: Procedural Programming Expert Systems: Principles and Programming, Fourth Edition.
ASP.NET Programming with C# and SQL Server First Edition Chapter 3 Using Functions, Methods, and Control Structures.
Python Programming Chapter 6: Iteration Saad Bani Mohammad Department of Computer Science Al al-Bayt University 1 st 2011/2012.
1 Session 3: Flow Control & Functions iNET Academy Open Source Web Programming.
Functional Programming and Lisp. Overview In a functional programming language, functions are first class objects. In a functional programming language,
Chapter Twenty-ThreeModern Programming Languages1 Formal Semantics.
Formal Semantics Chapter Twenty-ThreeModern Programming Languages, 2nd ed.1.
The Loop Macro Many of the CL “functions” are actually macros (let, progn, if, etc) The most complicated macro in CL is probably the Loop macro –The Loop.
Lecture 1-2CS251: Intro to AI/Lisp II “And now for something completely different…”
Saeed Ghanbartehrani Summer 2015 Lecture Notes #5: Programming Structures IE 212: Computational Methods for Industrial Engineering.
PRACTICAL COMMON LISP Peter Seibel 1.
Macros “How can you get anything done in [other languages], I think, without macros?” - Paul Graham, 2003.
Macros You might recall from earlier that some of the statements we use are not actually functions but macros –In CL, a function evaluates all of its parameters.
CSE S. Tanimoto Lisp Defining Macros in Lisp Extensibility: A language is extensible if the language can be extended. New Lisp control structures.
COMPUTER PROGRAMMING. Functions’ review What is a function? A function is a group of statements that is executed when it is called from some point of.
Python Functions.
Introduction to LISP. Lisp Extensible: It lets you define new operators yourself Lisp programs are expressed as lisp data structures –You can write programs.
Control Structures - Selections - Repetitions/iterations (part 2) 1 -Based on slides from Deitel & Associates, Inc. - Revised by T. A. Yang.
PRACTICAL COMMON LISP Peter Seibel 1.
1 Using Templates COSC 1567 C++ Programming Lecture 10.
JavaScript, Fourth Edition
UMBC CMSC Common Lisp II. UMBC CMSC Input and Output Print is the most primitive output function > (print (list 'foo 'bar)) (FOO BAR) The.
PRACTICAL COMMON LISP Peter Seibel 1.
Programming Fundamentals. Topics to be covered Today Recursion Inline Functions Scope and Storage Class A simple class Constructor Destructor.
1 Program Development  The creation of software involves four basic activities: establishing the requirements creating a design implementing the code.
 Control Flow statements ◦ Selection statements ◦ Iteration statements ◦ Jump statements.
Fall 2008Programming Development Techniques 1 Topic 17 Assignment, Local State, and the Environment Model of Evaluation Section 3.1 & 3.2.
1 COSC generating functions, templates, and macros Yves Lespérance Adapted from Peter Roosen-Runge.
Macros and general code walkers in Lisp: how useful! or, how useful? Ernst van Waning
Fortran: Control Structures Session Three ICoCSIS.
PRACTICAL COMMON LISP Peter Seibel 1.
Chapter 6: Looping. Objectives Learn about the loop structure Create while loops Use shortcut arithmetic operators Create for loops Create do…while loops.
PRACTICAL COMMON LISP Peter Seibel 1.
Macros CSC 358/ Outline  Homework #6  Macro Intro  Backquote  Macros  Modify macros  Read Macros.
4 - Conditional Control Structures CHAPTER 4. Introduction A Program is usually not limited to a linear sequence of instructions. In real life, a programme.
Clojure Macros. Homoiconicity All versions of Lisp, including Clojure, are homoiconic This means that there is no difference between the form of the data.
1 Outline Review Introduction to LISP Symbols and Numbers Lists Writing LISP Functions LISPWorks.
Defining Macros in Lisp
Lecture 15 (Notes by P. N. Hilfinger and R. Bodik)
Defining Macros in Lisp
Clojure Macros.
Peter Seibel Practical Common Lisp Peter Seibel
Abstraction and Repetition
Chap 7. Advanced Control Statements in Java
Common Lisp II.
Defining Macros in Scheme
Presentation transcript:

PRACTICAL COMMON LISP Peter Seibel 1

CHAPTER 8 MACROS: DEFINING YOUR OWN 2

MACRO EXPANSION TIME VS. RUNTIME Macro expansion time: The time when macros run Runtime: the time when regular code, including the code generated by macros, runs When Lisp is interpreted the distinction between macro expansion time and runtime is less clear because they're temporally intertwined ( 糾纏 ). The basic skeleton of a DEFMACRO: (defmacro name (parameter*) "Optional documentation string." body-form*) Like a function, a macro consists of a name, a parameter list, an optional documentation string, and a body of Lisp expressions. However, the job of a macro isn't to do anything directly. Its job is to translate a macro into code that does a particular thing. 3

A SAMPLE MACRO: DO-PRIMES For example: We'll write a macro do-primes that provides a looping construct similar to DOTIMES and DOLIST except that instead of iterating over integers or elements of a list, it iterates over successive prime numbers. First, we'll need two utility functions, one to test whether a given number is prime and another that returns the next prime number greater or equal to its argument. ;;to test whether a given number is prime (defun primep (number) (when (> number 1) (loop for fac from 2 to (isqrt number) never (zerop (mod number fac))))) ;; to return the next prime number greater or equal to its argument (defun next-prime (number) (loop for n from number when (primep n) return n)) 4

A SAMPLE MACRO: DO-PRIMES Now we can write the macro. Following the procedure outlined previously, we need at least one example of a call to the macro and the code into which it should expand. (do-primes (p 0 19) (format t "~d " p)) The expression expresses a loop that executes the body once each for each prime number greater or equal to 0 and less than or equal to 19, with the variable p holding the prime number. Without the do-primes macro, we could write such a loop with DO like this: (do ((p (next-prime 0) (next-prime (1+ p)))) ((> p 19)) (format t "~d " p)) NIL 5

MACRO PARAMETERS We could define do-primes with two parameters, one to hold the list and a &rest parameter to hold the body forms, and then take apart the list by hand: (defmacro do-primes (var-and-range &rest body) (let ((var (first var-and-range)) (start (second var-and-range)) (end (third var-and-range))) `(do ((,var (next-prime,start) (next-prime (1+,var)))) Note that the variables var, start, and end each hold a value, extracted from var-and-range, that's then interpolated into the backquote expression that generates do-primes's expansion. (do-primes (p 0 19) (format t "~d " p)) 6

MACRO PARAMETERS However, we don‘t need to take apart ( 拆開 ) var-and-range “by hand” because macro parameter lists are what are called destructuring parameter lists. Within a destructuring parameter list, a simple parameter name can be replaced with a nested parameter list. The parameters in the nested parameter list will take their values from the elements of the expression that would have been bound to the parameter the list replaced. For instance, we can replace var-and-range with a list (var start end), and the three elements of the list will automatically be destructured into those three parameters. Another special feature of macro parameter lists is that we can use &body as a synonym for &rest. (defmacro do-primes ((var start end) &body body) `(do ((,var (next-prime,start) (next-prime (1+,var)))) 7

GENERATING THE EXPANSION Because do-primes is a simple macro, after we've destructured the arguments, all that's left is to interpolate them into a template to get the expansion. For simple macros like do-primes, the special backquote syntax is perfect. For instance, `(,a b) might be read as (list a 'b). Table 8-1 shows some examples of backquoted expressions along with equivalent listbuilding code. 8

GENERATING THE EXPANSION Compare the backquoted version of do-primes to the following version, which uses explicit list-building code, we know that backquote is a big convenience. The backquoted version (defmacro do-primes ((var start end) &body body) `(do ((,var (next-prime,start) (next-prime (1+,var)))) The explicit list-building code (defmacro do-primes-a ((var start end) &body body) (append '(do) (list (list (list var (list 'next-prime start) (list 'next-prime (list '1+ var))))) (list (list (list '> var end))) body)) 9

Macros are defined with DEFMACRO. Its syntax is similar to DEFUN. Let’s define a simplified version of INCF to increment ordinary variables. Our macro will take a variable name as input and construct an expression to increment that variable by one. (defmacro simple-incf (var) (list ’setq var (list ’+ var 1))) (setf a 4) > (simple-incf a) 5 10

Now let’s modify SIMPLE-INCF to accept an optional second argument specifying the amount by which to increment the variable. We do this with the &OPTIONAL lambda-list keyword. The default amount to increment the variable will be one. (defmacro simple-incf (var &optional (amount 1)) (list ’setq var (list ’+ var amount))) (setf b 2) > (simple-incf b (* 3 a)) 17 11

Now let’s modify SIMPLE-INCF to accept an optional second argument specifying the amount by which to increment the variable. We do this with the &OPTIONAL lambda-list keyword. The default amount to increment the variable will be one. (defmacro simple-incf (var &optional (amount 1)) (list ’setq var (list ’+ var amount))) (setf b 2) > (simple-incf b (* 3 a)) 17 12

Macros do not evaluate their arguments, so the inputs to SIMPLE- INCF are the symbol B and the list (* 3 A), not the numbers 2 and (defmacro simple-incf (var &optional (amount 1)) (list ’setq var (list ’+ var amount)))

Let’s now consider why INCF has to be a macro rather than a function. Suppose we try to make an INCF function, using DEFUN. (defun faulty-incf (var) (setq var (+ var 1))) (setf a 7) > (faulty-incf a) 8 > (faulty-incf a) 8 > a 7 The input to FAULTY-INCF is the number seven. FAULTY-INCF creates a local variable named VAR to hold its input, and then it increments VAR by one. It doesn’t know anything about the variable A, because its argument was evaluated before the function was entered. 14

Write a SET-NIL macro that sets a variable to NIL. Use backquote to write a macro called SIMPLE-ROTATEF that switches the value of two variables. For example, if A is two and B is seven, then (SIMPLEROTATEF A B) should make A seven and B two. Obviously, setting A to B first, and then setting B to A won’t work. Your macro should expand into a LET expression that holds on to the original values of the two variables and then assigns them their new values in its body. 15

GENERATING THE EXPANSION Test correctness of the macro in two ways. Method 1: Run it. CL-USER> (do-primes (p 0 19) (format t "~d " p)) NIL Method 2: look at the expansion of a particular call. CL-USER> (macroexpand-1 '(do-primes (p 0 19) (format t "~d " p))) (DO ((P (NEXT-PRIME 0) (NEXT-PRIME (1+ P)))) ((> P 19)) (FORMAT T "~d " P)) T The function MACROEXPAND-1 takes any Lisp expression as an argument and returns the result of doing one level of macro expansion. 16

PLUGGING THE LEAKS Since writing a macro is a way of creating an abstraction, we need to make sure our macros don't leak( 漏洞 ) needlessly. For example: Suppose we were to call do-primes with an expression such as (random 100) in the end position. (do-primes (p 0 (random 100)) (format t "~d " p)) MACROEXPAND-1 shows that CL-USER> (macroexpand-1 '(do-primes (p 0 (random 100)) (format t "~d " p))) (DO ((P (NEXT-PRIME 0) (NEXT-PRIME (1+ P)))) ((> P (RANDOM 100))) (FORMAT T "~d " P)) T When this expansion code is run, RANDOM will be called each time the end test for the loop is evaluated. Thus this loop will iterate until it happens to draw a random number less than or equal to the current value of p. While the total number of iterations will still be random, it will be drawn from a much different distribution than the uniform distribution RANDOM returns. This is a leak in the abstraction because the caller needs to be aware that the end form is going to be evaluated more than once. 17

PLUGGING THE LEAKS We can fix the multiple evaluation easily enough; we just need to generate code that evaluates end once and saves the value in a variable to be used later. For example, we can fix the multiple evaluation problem with this definition: (defmacro do-primes ((var start end) &body body) `(do ((ending-value,end) (,var (next-prime,start) (next-prime (1+,var)))) ((>,var Recall that in a DO loop, variables defined with an initialization form and no step form don't change from iteration to iteration. Unfortunately, this fix introduces two new leaks to the macro abstraction. 18

PLUGGING THE LEAKS First new leak: Because the initialization forms for variables in a DO loop are evaluated in the order the variables are defined, when the macro expansion is evaluated, the expression passed as end will be evaluated before the expression passed as start, opposite to the order they appear in the macro call. This leak is trivially plugged by swapping the order of the two variable definitions. (defmacro do-primes ((var start end) &body body) `(do ((,var (next-prime,start) (next-prime (1+,var))) (ending-value,end)) ((>,var 19

PLUGGING THE LEAKS Second new leak: The leak was created by using the variable name ending-value. The problem is that the name can end up interacting with code passed to the macro or in the context where the macro is called. The following seemingly innocent( 無害的 ) call to do-primes doesn't work correctly because of this leak: (do-primes (ending-value 0 10) (print ending-value)) MACROEXPAND-1 can show the problem. Break 7 [16]> (macroexpand-1 '(do-primes (ending-value 0 19) (print ending-value))) (do ((ending-value (next-prime 0) (next-prime (1+ ending-value))) (ending-value 10)) ((> ending-value ending-value)) (print ending-value)) Some Lisps may reject this code because ending-value is used twice as a variable name in the same DO loop. If not rejected outright, the code will loop forever since ending-value will never be greater than itself. 20

PLUGGING THE LEAKS Second new leak: The following is another example: (let ((ending-value 0)) (do-primes (p 0 10) (incf ending-value p)) ending-value) Again, MACROEXPAND-1 can show the problem. (let ((ending-value 0)) (do ((p (next-prime 0) (next-prime (1+ p))) (ending-value 10)) ((> p ending-value)) (incf ending-value p)) ending-value) In this case the generated code is perfectly legal, but the behavior isn't at all what you want. Because the binding of ending-value established by the LET outside the loop is shadowed by the variable with the same name inside the DO, the form (incf ending-value p) increments the loop variable ending-value instead of the outer variable with the same name, creating another infinite loop. 21

PLUGGING THE LEAKS Clearly, what we need to patch this leak is a symbol that will never be used outside the code generated by the macro. The function GENSYM returns a unique symbol each time it's called. Thus, we can generate a new symbol each time do-primes is expanded. (defmacro do-primes ((var start end) &body body) (let ((ending-value-name (gensym))) `(do ((,var (next-prime,start) (next-prime (1+,var))) (,ending-value-name,end)) 22

PLUGGING THE LEAKS With this definition the two previously problematic forms expand into code that works the way you want. The first form: (do-primes (ending-value 0 10) (print ending-value)) expands into the following: (do ((ending-value (next-prime 0) (next-prime (1+ ending-value))) (#:g )) ((> ending-value #:g2141)) (print ending-value)) Now the variable used to hold the ending value is the gensymed symbol, #:g2141. The name of the symbol, G2141, was generated by GENSYM but isn't significant; the thing that matters is the object identity of the symbol. 23

PLUGGING THE LEAKS The other previously problematic form: (let ((ending-value 0)) (do-primes (p 0 10) (incf ending-value p)) ending-value) looks like this if you replace the do-primes form with its expansion: (let ((ending-value 0)) (do ((p (next-prime 0) (next-prime (1+ p))) (#:g )) ((> p #:g2140)) (incf ending-value p)) ending-value) Now, there's no leak since the ending-value variable bound by the LET surrounding the do-primes loop is no longer shadowed by any variables introduced in the expanded code. 24

PLUGGING THE LEAKS The rules to write a macro: Unless there's a particular reason to do otherwise, include any subforms in the expansion in positions that will be evaluated in the same order as the subforms appear in the macro call. Unless there's a particular reason to do otherwise, make sure subforms are evaluated only once by creating a variable in the expansion to hold the value of evaluating the argument form and then using that variable anywhere else the value is needed in the expansion. Use GENSYM at macro expansion time to create variable names used in the expansion. 25

Let’s write a macro SHOWVAR that displays the value of a variable, like this: (defun f (x y) (showvar x) (showvar y) (* x y)) > (f 3 7) The value of X is 3 The value of Y is 7 21 (defmacro showvar (var) ‘(format t "~&The value of ~S is ~S” ’,var,var)) 26

If a template element is preceded by a comma and an at sign the value of that element is spliced into the result that backquote constructs rather than being inserted. The value of the element must be a list. If only a comma is used, the element would be inserted as a single object, resulting in an extra level of parentheses. For examples: (setf name ’fred) (setf address ’(16 maple drive)) > ‘(,name lives at,address now) ;Inserting. (FRED LIVES AT (16 MAPLE DRIVE) NOW) > ‘(,name lives now) ;Splicing. (FRED LIVES AT 16 MAPLE DRIVE NOW) 27

Write a macro SET-MUTUAL that takes two variable names as input and expands into an expression that sets each variable to the name of the other. (SET-MUTUAL A B) should set A to ’B, and B to ’A. What’s the meaning of the following macro? (defmacro set-zero (&rest variables) #’(lambda (var) (list ’setf var 0)) variables) > (set-zero a b c) ? 28