Macros in JavaScript

September 21st, 2009

Edit: I submitted this post to Hacker News to get some feedback and there is some small discussion over there.

It seems people other than myself would like to have macros in JavaScript, which makes me happy. Not as happy as if macros were part of the ECMAScript standard, but it is nice to know I'm not alone.

I have been wondering about how macros in JavaScript would work for a while now, and since I had the day off from work today, I made a little proof-of-concept.

<html>
    <head>
        <style>
            .pass {
                color:green;
            }
            .fail {
                color:red;
            }
        </style>
    </head>
    <body>

        <ul id="results">
        </ul>

        <script type="text/javascript"
                src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
        </script>

        <script type="text/macroJS" id="macroJS">
            -> assert(val, desc) {
                << $("#results").append(
                    "<li class=\"" + (~> {
                        << !!val ?
                            "pass" :
                            "fail";
                        }()) + "\">" + desc + "</li>");
            }

            assert(typeof(~> {}) === "function",
                   "The '~>' syntax is a function with one argument called '_'.");

            assert(typeof(->() {}) === "function",
                   "The '->' syntax is short for 'function'.");

            assert((~> {
                << true;
            }()), "The '<<' syntax is short for 'return'.");

            assert((~> {
                var a = "broken";
                unless (false) {
                    a = "works";
                }
                << a === "works";
            }()), "'Unless' is the opposite of 'if'.");
        </script>

        <script type="text/javascript">
            eval((function (str) {
                return str.replace(/unless[\s]*?\(/g, "if (!")
                          .replace(/->/g, "function")
                          .replace(/~>/g, "function (_)")
                          .replace(/<</g, "return");
            }($("#macroJS").html())));
        </script>

    </body>

</html>

Demo

Of course, when you run the demo the descriptions in the assertions have been manipulated by the macro-expansion as well as the actual code, but the point is that the macros expand successfully.

I took the "->" and "~>" syntax from this interesting blog post, I came up with the "<<" syntax all by myself (!), and the "unless" statement is from the example in Peter Michaux's blog post.

The macros here are very simple syntax-hacks, and just a proof-of-concept, but I think it shouldn't be too hard to expand on these further. Since the replace method can take a function as a second argument that can access the grouped matches in your regexes, it is very possible to create more powerful macros.

Finally, would I consider using this technique for the code that I write at work or for my side projects? No, because the pain of debugging from inside an eval outweighs the potential benefits in this case. If you really want macros in JavaScript, your best bet is probably ParenScript.

« Previous Entry

Next Entry »

blog comments powered by Disqus


Recent Entries

Source Code Cartography on December 23rd, 2011

Pycco Needs a Loving Home on August 17th, 2011

Operational Transformation Part 2: Operations on April 5th, 2011

Operational Transformation: An Introduction on March 26th, 2011

JavaScript Timer Congestion on March 8th, 2011

OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance on December 31st, 2010

Pythonic JavaScript Methods on November 16th, 2010

Pycco and Zoolander now on PyPi on October 13th, 2010

Implementing Multiple Value Returns in JavaScript on October 8th, 2010

setTimeout Patterns in JavaScript on August 10th, 2010

Creative Commons License

Fork me on GitHub