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 »

View Comments


Recent Entries


TryParenScript.com

On July 23rd, 2010


In response to "A JavaScript Function Guard"

On July 19th, 2010


My Notes from John Resig's "jQuery Hack Day" Talk

On July 5th, 2010


Announcing Pocco

On June 29th, 2010


Yet Another Lisp

On June 25th, 2010


Recent Happenings: All Play and No Work

On June 21st, 2010


Arguments.callee considered extraneous

On June 2nd, 2010


Javascript, "bind", and "this"

On May 20th, 2010


Class-Based Views and Django

On May 19th, 2010


Introducing Zoolander

On May 2nd, 2010


Creative Commons License

Fork me on GitHub