{"id":564,"date":"2007-12-17T21:22:22","date_gmt":"2007-12-17T21:22:22","guid":{"rendered":"http:\/\/scientopia.org\/blogs\/goodmath\/2007\/12\/17\/macros-why-theyre-evil\/"},"modified":"2007-12-17T21:22:22","modified_gmt":"2007-12-17T21:22:22","slug":"macros-why-theyre-evil","status":"publish","type":"post","link":"http:\/\/www.goodmath.org\/blog\/2007\/12\/17\/macros-why-theyre-evil\/","title":{"rendered":"Macros: why they&#039;re evil"},"content":{"rendered":"<p> I&#8217;ve gotten both some comments and some e-mail from people in response to my mini-rant about Erlang&#8217;s macros. I started replying in comments, but it got long enough that I thought it made sense to promote it up to a top-level post. It&#8217;s not really an Erlang issue, but a general language issue, and my opinions concerning it end up getting into some programming language design and philosophy issues. If, like me, you think that things like that are fun, then read on!<\/p>\n<p><!--more--><\/p>\n<p> I&#8217;ll start at the absolute beginning. What&#8217;s a macro?<\/p>\n<p> A macro is a meta-structure in a language which defines a translation of the code to be performed before compiling or running that code. To give a simple example, let&#8217;s look at a simple C++ program:<\/p>\n<pre>\n#include &lt;iostream.h&gt;\n#define f(x) (2*x)\nint main() {\ncout &lt;&lt; f(3) &lt;&lt; endl;\n}\n<\/pre>\n<p> &#8220;<code>f<\/code>&#8221; is a simple macro. Whenever you write &#8220;<code>f(x)<\/code>&#8221; in a program, the<br \/>\npre-processor, which runs <em>before<\/em> the compiler, will replace it with &#8220;<code>(2*x)<\/code>&#8220;. So in the main function, the macro preprocessor replaces &#8220;<code>f(3)<\/code>&#8221; so that the program that gets compiled is actually the following:<\/p>\n<pre>\n#include &lt;iostream.h&gt;\nint main() {\ncout &lt;&lt; (2*3) &lt;&lt; endl;\n}\n<\/pre>\n<p> Whatever you put inside of &#8220;<code>f(...)<\/code>&#8221; will be put into the replacement.  In the case of C, the macros are <em>textual<\/em>: the compiler makes no attempt to verify that the body of the macro is in any way valid C++ code. It just does a purely textual substitution. This means that it&#8217;s possible to write a macro that generates something totally invalid and incomprehensible. If you invoke the above macro with &#8220;<code>f(+)<\/code>&#8220;, the preprocessor will expand it to &#8220;<code>(2*+)<\/code>&#8220;.<\/p>\n<p> Textual replacement macros are the trickiest and most error prone. More sophisticated macro systems, like the ones in Lisp, work on some form of parse tree. Lisp code can be represented in terms of the basic list types of lisp, and the macro system takes advantage of that to describe how to construct code in a macro by performing list manipulations. For example,  in CommonLisp I could use the following to define a simple &#8220;while&#8221; loop in terms of the built-in &#8220;do&#8221; loop:<\/p>\n<pre>\n(defmacro while (test &amp;rest body)\n(append (list 'do nil (list (list 'not test)))\nbody))\n<\/pre>\n<p> What this says is: when you see an expression that looks like a function call to the function &#8220;test&#8221;, assign the first parameter to the parameter &#8220;test&#8221;; assign a list containing the rest of the parameters to a variable named &#8220;body&#8221;, and then execute the macro code using those parameter bindings,<br \/>\ngenerating as a result the list form of a lisp syntax tree. So, for example, if you wrote &#8220;<code>(while (&lt; x 10) (setq x (+ x 1)))<\/code>&#8220;, the macro would evaluate the expression &#8220;<code>(append (list 'do nil (list (list 'not (&lt; x 10)))) (setq x (+ x 1)))<\/code>&#8220;, which would evaluate to &#8220;<code>(do () ((not (&lt; x 10))) (setq x (+ x 1)))<\/code>&#8220;, which is a valid Lisp loop.<\/p>\n<p> The Lisp macro system is amazingly powerful: you&#8217;ve got a full Turing-complete meta-programming system available to you for extending  the syntax of Lisp, and it&#8217;s been used for building a huge variety of amazing things. There&#8217;s an object-oriented programming system implemented in Lisp called CLOS whose original implementation used <em>no<\/em> compiler changes at all &#8211; just a lot of skeleton code, with definitions glued together using macros.<\/p>\n<p> So why do I dislike macros so much? I don&#8217;t, in theory. The problem is what happens in practice &#8211; most programming language macro systems &#8211; including the two example ones I described above, stink. What I dislike is how they&#8217;re generally built. You can fix <em>most<\/em> (but not all) of the problems caused by macros if you&#8217;ve got a good enough macro system. But because most macro systems rely on a simple expansion-and-substitution, they&#8217;re prone to creating all sort of problems. I&#8217;ll walk you through a few examples, to give you a sense of what I mean.<\/p>\n<p> The most obvious example of what&#8217;s wrong with substitution macros is that they can create all sorts of unfortunate interactions with the evaluation semantics of the system. Macro evaluations don&#8217;t work<br \/>\nthe same as function evaluations, even though they usually look the same. The unfortunate effect of<br \/>\nthis is that you can get very unexpected semantic results from using them. Here&#8217;s a typical example. Suppose I want a macro that doubles its parameter, regardless of type. Any type that supports<br \/>\nthe &#8220;+&#8221; operator should be usable for the macro. So if I give it a &#8220;3&#8221;, it should return &#8220;3+3&#8221; (which will evaluated by constant propagation to &#8220;6&#8221;); if you give it an &#8220;A&#8221;, it should return &#8220;A&#8221;+&#8221;A&#8221; (which evaluates to &#8220;AA&#8221;.) I can do that in C++ with:<\/p>\n<pre>\n#define double(x) (x+x)\n<\/pre>\n<p> Now, suppose I use that in a program:<\/p>\n<pre>\n#include &lt;iostream.h&gt;\n#define double(x) (x+x)\nint main() {\nfor (int i=0; i &lt; 10; i++) {\ncout &lt;&lt; \"doubling \" &lt;&lt; i &lt;&lt; \" gives \" &lt;&lt; double(i) &lt;&lt; endl;\n}\n}\n<\/pre>\n<p>That generates the output:<\/p>\n<pre>\ndoubling 0 gives 0\ndoubling 1 gives 2\ndoubling 2 gives 4\ndoubling 3 gives 6\ndoubling 4 gives 8\ndoubling 5 gives 10\ndoubling 6 gives 12\ndoubling 7 gives 14\ndoubling 8 gives 16\ndoubling 9 gives 18\n<\/pre>\n<p> So far, so good. Now suppose I get rid of the for loop, and write it in a C++-ish fashion<br \/>\nusing a while loop and the auto-increment operator:<\/p>\n<pre>\n#include &lt;iostream.h&gt;\n#define double(x) (x+x)\nint main() {\nint i = 0;\nwhile (i &lt; 10) {\ncout &lt;&lt; \"doubling \" &lt;&lt; i &lt;&lt; \" gives \" &lt;&lt; double(i++) &lt;&lt; endl;\n}\n}\n<\/pre>\n<p> If &#8220;double&#8221; were a function, this would generate the same result as the original<br \/>\nprogram. But since &#8220;double&#8221; is a macro, if we run it, we get:<\/p>\n<pre>\ndoubling 2 gives 0\ndoubling 4 gives 4\ndoubling 6 gives 8\ndoubling 8 gives 12\ndoubling 10 gives 16\n<\/pre>\n<p> We get a very odd, unexpected result. That&#8217;s because the expansion of &#8220;<code>double<\/code>&#8221; turns &#8220;<code>i++<\/code>&#8221; into<br \/>\n&#8220;<code>(i++ + i++)<\/code>&#8220;, incrementing &#8220;<code>i<\/code>&#8221; twice each iteration. That&#8217;s very typical of C-style macros: you can get very unexpected results from macros, because they can end up executing parameters either more than once, or not at all, so parameters with any kind of state-effects can cause all sorts of trouble. To make matters worse, the invisibility of macros means that unless you&#8217;ve seen the implementation of a library,<br \/>\nyou <em>don&#8217;t know<\/em> which calls are function calls and which are macro invocations, so you need to check implementations to figure out what kind of parameters you can use.<\/p>\n<p> You can make the argument that that&#8217;s sloppy programming: that passing a parameter expression that has a side effect is just bad, and shouldn&#8217;t be done. That&#8217;s not an unreasonable argument, except for the fact that you don&#8217;t always <em>know<\/em> when a parameter expression causes a side effect. I&#8217;ve been bitten more than once by things that are described as pure, idempotent functions, but which generate error messages &#8211; and having them generate the same message multiple times because of an unfortunate macro effect.<\/p>\n<p> But even if you ban side-effecting parameters to all calls, effectively eliminating the<br \/>\nissue above, there&#8217;s still one major potential source of grief in most macro systems. It&#8217;s a bit more<br \/>\nsubtle, and more serious because of its subtlety. You can use a macro for years with no ill effects, and then get screwed by this. It&#8217;s called <em>name capture<\/em>. A trivial example of it could happen with the example above: &#8220;double&#8221; is the name of a built-in type in C++, and &#8220;double&#8221; written as a function<br \/>\nis supposed to be a type conversion. But because we&#8217;ve implemented it as a macro, the type conversions aren&#8217;t going to do what you expect: they&#8217;ve been replaced, because the macro system captured the name.<\/p>\n<p> For a more interesting and subtle example of this, we can shift back to Lisp, suppose you used<br \/>\nmacros to implement a simple for loop. And suppose that you did it in a way that introduced a variable<br \/>\nin your macro, like the following:<\/p>\n<pre>\n(defmacro for (var init incr bound &amp;rest body)\n`(let ((,var ,init))\n(while (&lt; ,bound ,var)\n,@body\n(setq old-val ,var)\n(setq var (+ old-val ,incr)))))\n<\/pre>\n<p> In here, &#8220;<code>old-val<\/code>&#8221; is used as a counter to hold the previous value of the iteration. It&#8217;s not really<br \/>\nneeded here, but it allows me to illustrate the problem without getting into an overly complicated<br \/>\nexample. What happens if you use the &#8220;<code>for<\/code>&#8221; macro in a function which already has a variable named<br \/>\n&#8220;<code>old-var<\/code>&#8220;? What happens is a disaster: the macro &#8220;captures&#8221; the existing variable, and changes its<br \/>\nvalue. The variable that&#8217;s used in the macro-expansion is in the same namespace as variables in the<br \/>\ncontext where the macro is used, and so it can overlap or collide with existing variables. You&#8217;ll never<br \/>\nknow that this is possible until your program starts mysteriously getting wrong results, because you<br \/>\nfinally used the macro in a context where a name was already defined. To get around this, Lisp has a<br \/>\nbunch of functions that generate new symbols that are guaranteed never to have been seen before, but<br \/>\nwriting code that&#8217;s completely safe, so that it never accidentally captures or shadows an existing<br \/>\nvariable is a very tricky skill.<\/p>\n<p> There are ways around the syntactic capture issue, but they&#8217;re very rare. In fact, I really only know of one &#8211; the hygenic macro system in Scheme. (I&#8217;ve seen pretty much exactly the same thing crop up in some other Lisp variants, like Dylan, and even one prolog variant.) What these systems do is provide a sort of pattern matching facility that lets you grab parts of the expression and re-arrange them. But variables used in the macro are bound <em>at the point where the macro is declared<\/em>, not at the point where the macro is used. There&#8217;s usually a way to capture something if you specifically want to.<\/p>\n<p> For example, here&#8217;s a real Scheme macro for a functional &#8220;replace&#8221; loop, taken from <a href=\"http:\/\/c2.com\/cgi\/wiki?DefineSyntax\">here<\/a>:<\/p>\n<pre>\n(define-syntax replace\n(syntax-rules (initially with until just-before)\n((replace &lt;var&gt; initially &lt;value&gt; with &lt;newvalue&gt; until &lt;done&gt;)\n(let loop ((&lt;var&gt; &lt;value&gt;))\n(if &lt;done&gt; &lt;var&gt; (loop &lt;newvalue&gt;))))\n((replace &lt;var&gt; initially &lt;value&gt; with &lt;newvalue&gt; until just-before &lt;done&gt;)\n(let loop ((old #f) (&lt;var&gt; &lt;value&gt;))\n(if &lt;done&gt; old (loop &lt;var&gt; &lt;newvalue&gt;))))\n((replace &lt;var1&gt; &lt;var2&gt; initially &lt;value1&gt; &lt;value2&gt; with &lt;newvalue1&gt; &lt;newvalue2&gt; until &lt;done&gt;)\n(let loop ((&lt;var1&gt; &lt;value1&gt;) (&lt;var2&gt; &lt;value2&gt;))\n(if &lt;done&gt; (list &lt;var1&gt; &lt;var2&gt;)\n(loop &lt;newvalue1&gt; &lt;newvalue2&gt;))))))\n<\/pre>\n<p> The key to reading this is to understand that the names immediately after &#8220;syntax-rules&#8221; are<br \/>\ngoing to be <em>keyword<\/em> in the macro. After that, you have patterns built with the keywords and variables, paired with replacements. Expressions are matched against the patterns in sequence, with the keywords lined up, and the first one that matches the expression. So, for example, if you provided<br \/>\nthe above rule with &#8220;(replace x initially 1 with (+ x 1) until (&gt; x 10))&#8221;, it would match the first<br \/>\npattern, and bind &lt;var&gt; to &#8220;x&#8221;, &lt;value&gt; to &#8220;1&#8221;, &lt;newvalue&gt; to &#8220;(+ x 1)&#8221;, and &lt;done&gt; to &#8220;(&gt; x 10)&#8221;. The  resu0lt of the expansion would be:<br \/>\n<code>(let loop ((x 1)) (if (&gt; x 10) x (loop (+ x 1))))<\/code>. <\/p>\n<p> But there&#8217;s a problem even with hygenic macros, which is the fact that the code you run isn&#8217;t quite the code you wrote. When it comes to debugging, profiling, etc., the code that&#8217;s actually compiled can be quite different from the original code you wrote. You can&#8217;t single-step a debugger through a macro, or profile a macro. And it can be a huge surprise sometimes. To give you a sense of what I mean, here&#8217;s what happens when I ask CommonLisp to show me the result of expanding the &#8220;while&#8221; macro above invoked with <code>(while (&lt; x 10) (setq x (+ x 1)))<\/code>:<\/p>\n<pre>\n(BLOCK NIL\n(LET NIL\n(TAGBODY #:G7752 (IF (NOT (&lt; X 10)) (GO #:G7753)) (SETQ X (+ X 1)) (PSETQ)\n(GO #:G7752) #:G7753 (RETURN-FROM NIL (PROGN)))))\n<\/pre>\n<p> Looking at that, you could quite justifiably ask: &#8220;What the <em>hell<\/em> is that?&#8221;. The answer is<br \/>\nthat the &#8220;primitive&#8221; do-loop is actually also a macro. So macro expansion expanded them both out. And the end result is quite thoroughly incomprehensible. I&#8217;d <em>hate<\/em> to be confronted with that in a debugger! Particularly if the body of my loop was more complicated than a single assignment statement!<\/p>\n<p> The Scheme system comes <em>close<\/em> to addressing that. Because the macros are <em>so<\/em> strictly structured, you can get at least some correlation between elements of the macro and elements of the executable code. But the fact remains that the transformations can totally break that. Scheme macros are fully Turing-equivalent &#8211; you can do <em>anything<\/em> in a macro, and most things will result in a mess.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve gotten both some comments and some e-mail from people in response to my mini-rant about Erlang&#8217;s macros. I started replying in comments, but it got long enough that I thought it made sense to promote it up to a top-level post. It&#8217;s not really an Erlang issue, but a general language issue, and my [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[54],"tags":[],"class_list":["post-564","post","type-post","status-publish","format-standard","hentry","category-programming"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4lzZS-96","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/564","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/comments?post=564"}],"version-history":[{"count":0,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/564\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/media?parent=564"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/categories?post=564"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/tags?post=564"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}