Macros
This presentation is the property of its rightful owner.
Sponsored Links
1 / 15

Macros PowerPoint PPT Presentation


  • 135 Views
  • Uploaded on
  • Presentation posted in: General

Macros. “How can you get anything done in [other languages], I think, without macros?” - Paul Graham, 2003. Writing Programs…. … that write programs! Macros work via transformation Macros translate a given LISP expression into another expression Allows for efficient execution.

Download Presentation

Macros

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


Macros

Macros

“How can you get anything done in [other languages], I think, without macros?”

- Paul Graham, 2003


Writing programs

Writing Programs…

  • … that write programs!

  • Macros work via transformation

    • Macros translate a given LISP expression into another expression

  • Allows for efficient execution


Two similar functions

Two Similar Functions

(defun print-file (file)

(with-open-file (str file)

(do ((line (read-line str nil nil)

(read-line str nil nil)))

((null line) nil)

(format t "~A~%" line) )))

(defun find-first-comment (file)

(with-open-file (str file)

(do ((line (read-line str nil nil)

(read-line str nil nil)))

((null line) nil)

(when (eql (char line 0) #\;)

(return line)) )))


Desire to generalize

Desire to Generalize

  • If this pattern reoccurs often, we would like to do something simpler like

    (defun print-file (file)

    (dofile line file

    (format t "~A~%" line) ))

    (defun find-first-comment (file)

    (dofile line file

    (when (eql (char line 0) #\;)

    (return line)) ))

  • But how would you do this with a function? line is being passed as a variable name…


Solution is macros

Solution is Macros

  • What a function does

    • 1. Evaluate each argument

    • 2. Evaluate the body of the function

  • What a macro does

    • 1. Evaluate body of macro

    • 2. Evaluate resulting body of expanded macro


Defining macros

Defining Macros

  • Similar to defun

    (defmacro macro-name (argument*) S-expr*)

    (defmacro dofile (var file &rest body)

    (list 'with-open-file

    (list 'str file)

    (list* 'do

    (list (list var

    (list 'read-line

    'str 'nil 'nil)

    (list 'read-line

    'str 'nil 'nil)))

    (list (list 'null var) 'nil)

    body)))

  • But this is impossibly ugly


Backquote

Backquote

  • The backquote (`), like single quote ('), can be used to inhibit evaluation

    >`(a b c d)

    (A B C D)

    >`(setf x (* 3 7))

    (SETF X (* 3 7))

  • Unlike single quote, can specify partial evaluation by using a comma (,)

    >`(setf x ,(* 3 7))

    (SETF X 21)


Simplified macro with backquote

Simplified Macro with Backquote

  • We can make our macro definition more readable with the backquote

    (defmacro dofile (var file &rest body)

    `(with-open-file (str ,file)

    ,(list* 'do `((,var

    (read-line str nil nil)

    (read-line str nil nil)))

    `((null ,var) nil)

    body)))

  • Still kind of ugly. Problem is that body can be a list of commands, so we need list*


Splice

Splice

  • The splicing operator (,@) allows the outer parentheses of an expression to be removed

    >`(+ ,@(cdr '(2 4 6)))

    (+ 4 6)

  • Now we can fix our macro

    (defmacro dofile (var file &rest body)

    `(with-open-file (str ,file)

    (do ((,var (read-line str nil nil)

    (read-line str nil nil)))

    ((null ,var) nil)

    ,@body) ))


Macroexpand

Macroexpand

  • A tool for debugging macros, it expands a macro and suppresses second evaluation, returning the expanded expression

    >(defmacro front (some-list)

    (list 'car some-list))

    FRONT

    >(macroexpand '(front '(1 2 3)))

    (CAR '(1 2 3))

    T

  • If your macro definition contains macros in it, then they will also be expanded, which may result in code you don’t recognize


Specializing the input

Specializing the Input

  • Currently we call our macro using

    (dofile var file body*)

  • But we can group our arguments like so

    (dofile (var file) body*)

  • If we parenthesize them in the macro definition

  • Also, in macros, &body is preferred in place of &rest, for formatting reasons. So now we have:

    (defmacro dofile ((var file)&body body)

    `(with-open-file (str ,file)

    (do ((,var (read-line str nil nil)

    (read-line str nil nil)))

    ((null ,var) nil)

    ,@body) ))


Problems 1

Problems (1)

  • This code works:

    (defun copy (source out)

    (with-open-file (outstr out :direction :output)

    (dofile (line source)

    (format outstr "~A~%" line))))

  • But this almost identical code doesn’t

    (defun copy (source out)

    (with-open-file (str out :direction :output)

    (dofile (line source)

    (format str "~A~%" line))))


Problems 2

Problems (2)

  • If we expand the macro definition, we see why

    (defun copy (source out)

    (with-open-file (str out :direction :output)

    (with-open-file (str source)

    (do ((line (read-line str nil nil)

    (read-line str nil nil)))

    ((null line) nil)

    (format str "~A~%" line))) ))

  • There are name collisions between the variables in the macro and those in the function definition.

  • The only way to avoid this is by knowing the internals of the macro, which is unacceptable. We need to fix the macro again


Gensym

Gensym

  • gensym generates a new symbol name that is guaranteed to be unique

  • We use it to get unique variable names for our macro (changes in red)

    (defmacro dofile ((var file) &body body)

    (let ((str (gensym)))

    `(with-open-file (,str ,file)

    (do ((,var (read-line ,str nil nil)

    (read-line ,str nil nil)))

    ((null ,var) nil)

    ,@body) )))


Macroexpand after gensym

Macroexpand After Gensym

>(macroexpand '(dofile (line "file.txt") nil))

(LET ((#:G1478 (OPEN "file.txt")))

(UNWIND-PROTECT

(PROGN

(BLOCK ()

(LET ((LINE (READ-LINE #:G1478 NIL NIL)))

(TAGBODY

#:G1479

(IF (NULL LINE) (RETURN (PROGN NIL)))

(TAGBODY NIL)

(PSETQ LINE (READ-LINE #:G1478 NIL NIL))

(GO #:G1479)))))

(IF #:G1478 (CLOSE #:G1478))))


  • Login