Macros | Common Lisp Docs (2024)

One of the most distinctive features of Common Lisp is its macros. Macros are special functions which operate on code before it is compiled. In most other languages, code is strutured according to a fairly complex syntax, which is parsed to generate an abstract syntax tree. This is designed to be used internally by the compiler or interpreter and writing functions which operate on code is generally very challenging.Lisp code is already structed as a tree, written with s-expressions and read in as a familiar cons structure which can be manipulated in the same way as any other data. This equivalance between code and data is described by the Greek word "hom*oiconicity" meaning "same representation", and it makes Lisp macros particularly powerful and easy to write.

Macros have multiple uses. They can introduce new syntax to the language, control when code is evaluated and how many times, or make programs more efficient by doing computation at compile time. This tutorial will explore some simple but useful examples.

And

Suppose we have some lisp forms and we want to check they all evaluate to true. The obvious way to write this is (and a b c), where a b and c are some arbitrary lisp forms. We could implement this as a function, using rest parameters and recursion to operate on an arbitrary number of values before returning either the final value, or nil if any of the other values are nil.

(defun and (&rest values)
(when (car values)
(if (cdr values)
(and (cdr values))
(car values))))
* (and (< 1 2) 3)
3
* (and (> 1 2) 3)
nil

This seems to work, but suppose we are writing a control system for a rocket silo. We want to perform a sequence of operations, checking each stage returns a true value to indicate it has been successfully completed.

* (and (open-doors) (prime-fuel-tanks) (launch-rocket))

Order of evaluation

Why did our silo just blow up?

These are all functions with side effects. Calling open-doors sends a signal to open the doors and then returns t if sensors indicate the doors have opened correctly or nil otherwise. When a function is called all of its arguments are all evaluated first, followed by the body of the function. This means that our and expression will try to open the doors, prime the fuel tanks and launch the rocket, and only afterwards check the doors opened successfully.

With a macro we can control exactly when the evaluation happens

(defmacro %and (&rest forms)
(list 'when (car forms)
(if (cddr forms)
(cons '%and (cdr forms))
(cadr forms))))

Our missile silo controller is expanded at compile time to

(if (open-doors)
(if (prime-fuel-tanks)
(launch-rocket)))

If an argument evaluates to nil, the chain of if statements is broken and none of the remaining arguments are evaulated.

Notice that (when a b) is itself a macro invocation, which expands to (if a b). Many expressions which require special syntax in other languages are implemented as macros in Lisp. The AND macro is part of the language specification which means it is provided by all Common Lisp implementations, usually with code similar to our example.

Backquote

Common Lisp's backquote syntax is very useful tool for constructing code in macros and complex data structures generally. Instead of constructing a data structure with functions like cons list and append, one can simple write a quoted template and insert values into it by unquoting with commas.For instance, instead of

(append '(there will be) (incf n) '(green bottles standing on the wall))

With backquote we can write

* (defvar n 10)
N
* `(there will be ,(decf n) green bottles standing on the wall)
(there will be 9 green bottles standing on the wall)
* `(there will be ,(decf n) green bottles standing on the wall)
(there will be 8 green bottles standing on the wall)

The comma unquotes the expression following it so it is evaluated as if it were outside the quote. n is decremented and the result is inserted into the quoted structure.

Lists can also be unquoted with ,@ which splices the contents of the list into the surrounding structure. Compare

* (defvar object '(green bottle))
OBJECT
* `(there will be 1 ,object standing on the wall)
(there will be 1 (green bottle) standing on the wall)
* `(there will be 1 ,@object standing on the wall)
(there will be 1 green bottle standing on the wall)

Backquote is extremely useful for generating code in macros. Subsequent examples will make heavy use of it.

Comparing numbers

Quite often a program needs to compare two numbers and do something different depending on which is larger. In Lisp we could use a cond form like this

(cond ((< x y) (y-is-bigger))
((= x y) (both-equal))
(t (x-is-bigger)))

This is quite long winded for such a common pattern. It would be clearer and more convenient if we could write something like (compare (x y) (y-is-bigger) (x-is-bigger) (both-equal)). Luckily with macros we can.

(defmacro compare ((a b) < = >)
`(cond ((< ,a ,b) ,<)
((= ,a ,b) ,=)
(t ,>)))

Destructuring lambda lists

Unlike ordinary functions where arguments are interpreted and bound according to an ordinary lambda list, the arguments to a macro are destructured, and defined by a destructuring lambda list. This means we can define a macro which takes structured arguments and have the variables bound automatically. In compare we have put a and b into their own list for clarity.

* (let ((x 100)
(y 10))
(compare (x y) 'y-is-bigger 'both-equal 'x-is-bigger))
X-IS-BIGGER

This appears to work but there is a problem.

* (let ((x 11)
(y 10))
(compare ((decf x) y) 'b-is-bigger 'both-equal 'a-is-bigger))
A-IS-BIGGER

Unintentional repeated evaluation

If x is 11, (decf x) will decrement x and return 10, so the result we would expect is BOTH-EQUAL.What is happening is that when the first test, (< ,a ,b), fails, a and b are evaluated again in the second test where we have (= ,a ,b). Arguments to a macro are not evaluated until after the macro is expanded, so if our macro returns a form which contains a twice, it will be evaluated twice. For functions which have side effects or require a lot of computation, this is not good.

We can solve this problem by generating code to evaluate a and b and bind them to variables before comparing them.

(defmacro compare ((a b) < = >)
`(let ((a ,a)
(b ,b))
(cond ((< a b) ,<)
((= a b) ,=)
(t ,>))))
* (let ((x 11)
(y 10))
(compare ((decf x) y) 'b-is-bigger 'both-equal 'a-is-bigger))
BOTH-EQUAL

Unintentional variable capture

Good, now (decf x) is only performed once and the result is equal to y. Have we finished? How about this?

* (let ((x 10)
(y 100)
(a 'y-is-bigger)
(b 'both-equal)
(c 'x-is-bigger))
(compare (x y) a b c))
10

What has happened here? We expected to get back the value of a: 'y-is-bigger. Instead of which the value of x has leaked through, replacing the value we supplied. Let's look at the expansion of this macro (we can get the result of expanding a macro at runtime with the macroexpand function)

* (macroexpand '(compare (x y) a b c))
(LET ((A X) (B Y))
(COND ((< A B) A) ((= A B) B) (T C)))

Gensym

Looking at the expanded form it's quite obvious what is going wrong. x is bound to a in the code produced by the macro which overrides the outer binding of a to 'y-is-bigger. We could get around this by coming up with more obscure names for bindings in macros, but there is a better solution. The function gensym returns a new uninterned symbol. Gensyms are guarenteed to be unique because no uninterned symbol is eq to any other, so if we bind a value to a gensym, we can be certain no other variables will be accidentally captured.

(defmacro compare ((a b) < = >)
(let ((asym (gensym))
(bsym (gensym)))
`(let ((,asym ,a)
(,bsym ,b))
(cond ((< ,asym ,bsym) ,<)
((= ,asym ,bsym) ,=)
(t ,>)))))

Once-only

This can be expressed far more elegantly using the once-only macro, originally written by Peter Norvig and available in the Alexandria utility library. once-only automatically introduces gensym bindings for expressions in exactly the same way as the previous example.

(require 'alexandria)
(import 'alexandria:once-only)

(defmacro compare ((a b) < = >)
(once-only (a b)
`(cond ((< ,a ,b) ,<)
((= ,a ,b) ,=)
(t ,>))))

Understanding Macros

Time of Evaluation

\

The , Comma, ` Back Quote, and ' Quote operators

The @ Splice operator

Declaring Variables with GENSYM

Variable Capture

Classic Macro Pitfalls

Reader Macros

Macros | Common Lisp Docs (2024)

References

Top Articles
Top Things to Do - Crescent City - Del Norte County Chamber of Commerce
Getaway Guide: Spend a Weekend in Del Norte, Colorado | OutThere Colorado
Tiny Tina Deadshot Build
Methstreams Boxing Stream
Monthly Forecast Accuweather
Wordscapes Level 5130 Answers
oklahoma city for sale "new tulsa" - craigslist
When is streaming illegal? What you need to know about pirated content
Delectable Birthday Dyes
Over70Dating Login
Toonily The Carry
Tiger Island Hunting Club
Phillies Espn Schedule
Full Range 10 Bar Selection Box
Our Facility
Signs Of a Troubled TIPM
Nj Scratch Off Remaining Prizes
What is Cyber Big Game Hunting? - CrowdStrike
2015 Honda Fit EX-L for sale - Seattle, WA - craigslist
No Hard Feelings Showtimes Near Cinemark At Harlingen
Kylie And Stassie Kissing: A Deep Dive Into Their Friendship And Moments
Gopher Hockey Forum
Project, Time & Expense Tracking Software for Business
John Chiv Words Worth
R&S Auto Lockridge Iowa
Silky Jet Water Flosser
Kentuky Fried Chicken Near Me
Water Temperature Robert Moses
Mami No 1 Ott
Dl.high Stakes Sweeps Download
Allegheny Clinic Primary Care North
Ripsi Terzian Instagram
Eero Optimize For Conferencing And Gaming
Workboy Kennel
Vip Lounge Odu
Black Adam Showtimes Near Amc Deptford 8
Junee Warehouse | Imamother
2024 Ford Bronco Sport for sale - McDonough, GA - craigslist
Are you ready for some football? Zag Alum Justin Lange Forges Career in NFL
20 Best Things to Do in Thousand Oaks, CA - Travel Lens
3302577704
About :: Town Of Saugerties
Vocabulary Workshop Level B Unit 13 Choosing The Right Word
Colorado Parks And Wildlife Reissue List
Csgold Uva
Clock Batteries Perhaps Crossword Clue
Barber Gym Quantico Hours
10 Bedroom Airbnb Kissimmee Fl
Uno Grade Scale
Stone Eater Bike Park
Texas 4A Baseball
Latest Posts
Article information

Author: Barbera Armstrong

Last Updated:

Views: 6394

Rating: 4.9 / 5 (79 voted)

Reviews: 94% of readers found this page helpful

Author information

Name: Barbera Armstrong

Birthday: 1992-09-12

Address: Suite 993 99852 Daugherty Causeway, Ritchiehaven, VT 49630

Phone: +5026838435397

Job: National Engineer

Hobby: Listening to music, Board games, Photography, Ice skating, LARPing, Kite flying, Rugby

Introduction: My name is Barbera Armstrong, I am a lovely, delightful, cooperative, funny, enchanting, vivacious, tender person who loves writing and wants to share my knowledge and understanding with you.