<titledata-rh="true">Macros | Common Lisp Docs</title><metadata-rh="true"name="viewport"content="width=device-width,initial-scale=1"><metadata-rh="true"name="twitter:card"content="summary_large_image"><metadata-rh="true"property="og:image"content="https://lisp-docs.github.io/img/1024px-Lisp_logo.svg.png"><metadata-rh="true"name="twitter:image"content="https://lisp-docs.github.io/img/1024px-Lisp_logo.svg.png"><metadata-rh="true"property="og:url"content="https://lisp-docs.github.io/docs/tutorial/macros"><metadata-rh="true"property="og:locale"content="en"><metadata-rh="true"name="docusaurus_locale"content="en"><metadata-rh="true"name="docsearch:language"content="en"><metadata-rh="true"name="google-site-verification"content="Vzaw013_bfdKeUVG89Ch3W1zC9_vH9ID2dPB9Dz0vr0"><metadata-rh="true"name="docusaurus_version"content="current"><metadata-rh="true"name="docusaurus_tag"content="docs-default-current"><metadata-rh="true"name="docsearch:version"content="current"><metadata-rh="true"name="docsearch:docusaurus_tag"content="docs-default-current"><metadata-rh="true"property="og:title"content="Macros | Common Lisp Docs"><metadata-rh="true"name="description"content="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."><metadata-rh="true"property="og:description"content="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."><linkdata-rh="true"rel="icon"href="../../img/favicon.ico"><linkdata-rh="true"rel="canonical"href="macros.html"><linkdata-rh="true"rel="alternate"href="macros.html"hreflang="en"><linkdata-rh="true"rel="alternate"href="macros.html"hreflang="x-default"><linkdata-rh="true"rel="preconnect"href="https://C1F2Q5VM6X-dsn.algolia.net"crossorigin="anonymous"><linkrel="alternate"type="application/rss+xml"href="../../blog/rss.xml"title="Common Lisp Docs RSS Feed">
<linkrel="alternate"type="application/atom+xml"href="../../blog/atom.xml"title="Common Lisp Docs Atom Feed">
<script>!function(){functiont(t){document.documentElement.setAttribute("data-theme",t)}vare=function(){try{returnnewURLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{returnlocalStorage.getItem("theme")}catch(t){}}();t(null!==e?e:"light")}(),function(){try{constc=newURLSearchParams(window.location.search).entries();for(var[t,e]ofc)if(t.startsWith("docusaurus-data-")){vara=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><divid="__docusaurus"><divrole="region"aria-label="Skip to main content"><aclass="skipToContent_fXgn"href="macros.html#__docusaurus_skipToContent_fallback">Skip to main content</a></div><navaria-label="Main"class="navbar navbar--fixed-top"><divclass="navbar__inner"><divclass="navbar__items"><buttonaria-label="Toggle navigation bar"aria-expanded="false"class="navbar__toggle clean-btn"type="button"><svgwidth="30"height="30"viewBox="0 0 30 30"aria-hidden="true"><pathstroke="currentColor"stroke-linecap="round"stroke-miterlimit="10"stroke-width="2"d="M4 7h22M4 15h22M4 23h22"></path></svg></button><aclass="navbar__brand"href="../../index.html"><divclass="navbar__logo"><imgsrc="../../img/logo.svg"alt="Lisp Logo"class="themedComponent_mlkZ themedComponent--light_NVdE"><imgsrc="../../img/logo.svg"alt="Lisp Logo"class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><bclass="navbar__title text--truncate">Common Lisp Docs</b></a><aaria-current="page"class="navbar__item navbar__link navbar__link--active"href="../tutorial.html">Tutorial</a><ahref="../../cl-language-reference/index.html"target="_blank"rel="noopener noreferrer"class="navbar__item navbar__link">Technical Reference</a><aclass="navbar__item navbar__link"href="../whylisp.html">Why Lisp?</a><aclass="navbar__item navbar__link"href="../howto.html">Guides</a></div><divclass="navbar__items navbar__items--right"><aclass="navbar__item navbar__link"href="../contribute.html">Contribute!</a><aclass="navbar__item navbar__link"href="../help.html">Getting Help</a><aclass="navbar__item navbar__link"href="../about.html">About</a><aclass="navbar__item navbar__link"href="../../blog.html">Blog</a><ahref="https://github.com/lisp-docs"target="_blank"rel="noopener noreferrer"class="navbar__item navbar__link">GitHub<svgwidth="13.5"height="13.5"aria-hidden="true"viewBox="0 0 24 24"class="iconExternalLink_nPIU"><pathfill="currentColor"d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a><divclass="toggle_vylO colorModeToggle_DEke"><buttonclass="clean-btn toggleButton_gllP toggleButtonDisabled_aARS"type="button"disabled=""title="Switch between dark and light mode (currently light mode)"aria-label="Switch between dark and light mode (currently light mode)"aria-live="polite"><svgviewBox="0 0 24 24"width="24"height="24"class="lightToggleIcon_pyhR"><pathfill="currentColor"d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"></path></svg><svgviewBox="0 0 24 24"width="24"height="24"class="d
<p>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 "homoiconicity" meaning "same representation", and it makes Lisp macros particularly powerful and easy to write.</p>
<p>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.</p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id="and">And<ahref="macros.html#and"class="hash-link"aria-label="Direct link to And"title="Direct link to And"></a></h2>
<p>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.</p>
<p>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.</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="order-of-evaluation">Order of evaluation<ahref="macros.html#order-of-evaluation"class="hash-link"aria-label="Direct link to Order of evaluation"title="Direct link to Order of evaluation"></a></h3>
<p>Why did our silo just blow up?</p>
<p>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 <code>and</code> expression will try to open the doors, prime the fuel tanks and launch the rocket, and only afterwards check the doors opened successfully.</p>
<p>With a macro we can control exactly when the evaluation happens</p>
<p>If an argument evaluates to nil, the chain of <code>if</code> statements is broken and none of the remaining arguments are evaulated.</p>
<p>Notice that <code>(when a b)</code> is itself a macro invocation, which expands to <code>(if a b)</code>. 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.</p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id="backquote">Backquote<ahref="macros.html#backquote"class="hash-link"aria-label="Direct link to Backquote"title="Direct link to Backquote"></a></h2>
<p>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 <code>cons</code><code>list</code> and <code>append</code>, one can simple write a quoted template and insert values into it by unquoting with commas.
<divclass="language-lisp codeBlockContainer_Ckt0 theme-code-block"style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><divclass="codeBlockContent_biex"><pretabindex="0"class="prism-code language-lisp codeBlock_bY9V thin-scrollbar"style="color:#393A34;background-color:#f6f8fa"><codeclass="codeBlockLines_e6Vv"><spanclass="token-line"style="color:#393A34"><spanclass="token plain">* </span><spanclass="token punctuation"style="color:#393A34">(</span><spanclass="token defvar keyword"style="color:#00009f">defvar</span><spanclass="token defvar"></span><spanclass="token defvar variable"style="color:#36acaa">n</span><spanclass="token plain"></span><spanclass="token number"style="color:#36acaa">10</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"></span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain">N</span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain">* </span><spanclass="token punctuation"style="color:#393A34">`(</span><spanclass="token car">there</span><spanclass="token plain"> will be </span><spanclass="token punctuation"style="color:#393A34">,(</span><spanclass="token car">decf</span><spanclass="token plain"> n</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"> green bottles standing on the wall</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"></span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain"></span><spanclass="token punctuation"style="color:#393A34">(</span><spanclass="token car">there</span><spanclass="token plain"> will be </span><spanclass="token number"style="color:#36acaa">9</span><spanclass="token plain"> green bottles standing on the wall</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"></span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain">* </span><spanclass="token punctuation"style="color:#393A34">`(</span><spanclass="token car">there</span><spanclass="token plain"> will be </span><spanclass="token punctuation"style="color:#393A34">,(</span><spanclass="token car">decf</span><spanclass="token plain"> n</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"> green bottles standing on the wall</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"></span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain"></span><spanclass="token punctuation"style="color:#393A34">(</span><spanclass="token car">there</span><spanclass="token plain"> will be </span><spanclass="token number"style="color:#36acaa">8</span><spanclass="token plain"> green bottles standing on the wall</span><spanclass="token punctuation"style="color:#393A34">)</span><br></span></code></pre><divclass="buttonGroup__atx"><buttontype="button"aria-label="Copy code to clipboard"title="Copy"class="clean-btn"><spanclass="copyButtonIcons_eSgA"aria-hidden="true"><svgviewBox="0 0 24 24"class="copyButtonIcon_y97N"><pathfill="currentColor"d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svgviewBox="0 0 24 24"class="copyButtonSuccessIcon_LjdS"><pathfill="currentColor"d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>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.</p>
<p>Lists can also be unquoted with <code>,@</code> which splices the contents of the list into the surrounding structure. Compare</p>
<divclass="language-lisp codeBlockContainer_Ckt0 theme-code-block"style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><divclass="codeBlockContent_biex"><pretabindex="0"class="prism-code language-lisp codeBlock_bY9V thin-scrollbar"style="color:#393A34;background-color:#f6f8fa"><codeclass="codeBlockLines_e6Vv"><spanclass="token-line"style="color:#393A34"><spanclass="token plain">* </span><spanclass="token punctuation"style="color:#393A34">(</span><spanclass="token defvar keyword"style="color:#00009f">defvar</span><spanclass="token defvar"></span><spanclass="token defvar variable"style="color:#36acaa">object</span><spanclass="token plain"></span><spanclass="token punctuation"style="color:#393A34">'(</span><spanclass="token car">green</span><spanclass="token plain"> bottle</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"></span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain">OBJECT</span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain">* </span><spanclass="token punctuation"style="color:#393A34">`(</span><spanclass="token car">there</span><spanclass="token plain"> will be </span><spanclass="token number"style="color:#36acaa">1</span><spanclass="token plain"></span><spanclass="token splice symbol variable"style="color:#36acaa">,object</span><spanclass="token plain"> standing on the wall</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"></span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain"></span><spanclass="token punctuation"style="color:#393A34">(</span><spanclass="token car">there</span><spanclass="token plain"> will be </span><spanclass="token number"style="color:#36acaa">1</span><spanclass="token plain"></span><spanclass="token punctuation"style="color:#393A34">(</span><spanclass="token car">green</span><spanclass="token plain"> bottle</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"> standing on the wall</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"></span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain">* </span><spanclass="token punctuation"style="color:#393A34">`(</span><spanclass="token car">there</span><spanclass="token plain"> will be </span><spanclass="token number"style="color:#36acaa">1</span><spanclass="token plain"></span><spanclass="token splice symbol variable"style="color:#36acaa">,@object</span><spanclass="token plain"> standing on the wall</span><spanclass="token punctuation"style="color:#393A34">)</span><spanclass="token plain"></span><br></span><spanclass="token-line"style="color:#393A34"><spanclass="token plain"></span><spanclass="token punctuation"style="color:#393A34">(</span><spanclass="token car">there</span><spanclass="token plain"> will be </span><spanclass="token number"style="color:#36acaa">1</span><spanclass="token plain"> green bottle standing on the wall</span><spanclass="token punctuation"style="color:#393A34">)</span><br></span></code></pre><divclass="buttonGroup__atx"><buttontype="button"aria-label="Copy code to clipboard"title="Copy"class="clean-btn"><spanclass="copyButtonIcons_eSgA"aria-hidden="true"><svgviewBox="0 0 24 24"class="copyButtonIcon_y97N"><pathfill="currentColor"d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svgviewBox="0 0 24 24"class="copyButtonSuccessIcon_LjdS"><pathfill="currentColor"d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Backquote is extremely useful for generating code in macros. Subsequent examples will make heavy use of it.</p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id="comparing-numbers">Comparing numbers<ahref="macros.html#comparing-numbers"class="hash-link"aria-label="Direct link to Comparing numbers"title="Direct link to Comparing numbers"></a></h2>
<p>Quite often a program needs to compare two numbers and do something different depending on which is larger. In Lisp we could use a <code>cond</code> form like this</p>
<p>This is quite long winded for such a common pattern. It would be clearer and more convenient if we could write something like <code>(compare (x y) (y-is-bigger) (x-is-bigger) (both-equal))</code>. Luckily with macros we can.</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="destructuring-lambda-lists">Destructuring lambda lists<ahref="macros.html#destructuring-lambda-lists"class="hash-link"aria-label="Direct link to Destructuring lambda lists"title="Direct link to Destructuring lambda lists"></a></h3>
<p>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 <code>compare</code> we have put <code>a</code> and <code>b</code> into their own list for clarity.</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="unintentional-repeated-evaluation">Unintentional repeated evaluation<ahref="macros.html#unintentional-repeated-evaluation"class="hash-link"aria-label="Direct link to Unintentional repeated evaluation"title="Direct link to Unintentional repeated evaluation"></a></h3>
<p>If x is 11, <code>(decf x)</code> will decrement <code>x</code> and return 10, so the result we would expect is BOTH-EQUAL.
What is happening is that when the first test, <code>(< ,a ,b)</code>, fails, a and b are evaluated again in the second test where we have <code>(= ,a ,b)</code>. Arguments to a macro are not evaluated until after the macro is expanded, so if our macro returns a form which contains <code>a</code> twice, it will be evaluated twice. For functions which have side effects or require a lot of computation, this is not good.</p>
<p>We can solve this problem by generating code to evaluate a and b and bind them to variables before comparing them.</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="unintentional-variable-capture">Unintentional variable capture<ahref="macros.html#unintentional-variable-capture"class="hash-link"aria-label="Direct link to Unintentional variable capture"title="Direct link to Unintentional variable capture"></a></h3>
<p>Good, now <code>(decf x)</code> is only performed once and the result is equal to y. Have we finished? How about this?</p>
<p>What has happened here? We expected to get back the value of a: <code>'y-is-bigger</code>. 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 <code>macroexpand</code> function)</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="gensym">Gensym<ahref="macros.html#gensym"class="hash-link"aria-label="Direct link to Gensym"title="Direct link to Gensym"></a></h3>
<p>Looking at the expanded form it's quite obvious what is going wrong. <code>x</code> is bound to <code>a</code> 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 <code>gensym</code> 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.</p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="once-only">Once-only<ahref="macros.html#once-only"class="hash-link"aria-label="Direct link to Once-only"title="Direct link to Once-only"></a></h3>
<p>This can be expressed far more elegantly using the <code>once-only</code> macro, originally written by Peter Norvig and available in the Alexandria utility library. <code>once-only</code> automatically introduces gensym bindings for expressions in exactly the same way as the previous example.</p>
<h2class="anchor anchorWithStickyNavbar_LWe7"id="understanding-macros">Understanding Macros<ahref="macros.html#understanding-macros"class="hash-link"aria-label="Direct link to Understanding Macros"title="Direct link to Understanding Macros"></a></h2>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="time-of-evaluation">Time of Evaluation<ahref="macros.html#time-of-evaluation"class="hash-link"aria-label="Direct link to Time of Evaluation"title="Direct link to Time of Evaluation"></a></h3>
<p>\ </p>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="the--comma--back-quote-and--quote-operators">The <code>,</code> Comma, <code>`</code> Back Quote, and <code>'</code> Quote operators<ahref="macros.html#the--comma--back-quote-and--quote-operators"class="hash-link"aria-label="Direct link to the--comma--back-quote-and--quote-operators"title="Direct link to the--comma--back-quote-and--quote-operators"></a></h3>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="the--splice-operator">The <code>@</code> Splice operator<ahref="macros.html#the--splice-operator"class="hash-link"aria-label="Direct link to the--splice-operator"title="Direct link to the--splice-operator"></a></h3>
<h3class="anchor anchorWithStickyNavbar_LWe7"id="declaring-variables-with-gensym">Declaring Variables with GENSYM<ahref="macros.html#declaring-variables-with-gensym"class="hash-link"aria-label="Direct link to Declaring Variables with GENSYM"title="Direct link to Declaring Variables with GENSYM"></a></h3>
<h2class="anchor anchorWithStickyNavbar_LWe7"id="variable-capture">Variable Capture<ahref="macros.html#variable-capture"class="hash-link"aria-label="Direct link to Variable Capture"title="Direct link to Variable Capture"></a></h2>
<h2class="anchor anchorWithStickyNavbar_LWe7"id="classic-macro-pitfalls">Classic Macro Pitfalls<ahref="macros.html#classic-macro-pitfalls"class="hash-link"aria-label="Direct link to Classic Macro Pitfalls"title="Direct link to Classic Macro Pitfalls"></a></h2>