Skip to content
 

Praising macros

Macros are an indispensable programming feature. While procedures (functions, methods, messages etc.) let developers reuse common computation patterns, macros allow the abstraction of common syntactic constructions. These constructions can be as simple as a new control operator, or can be full domain-specific languages. For instance, there is no when operator in Scheme, but a lot of people like it. It simply tests a condition and execute a block of code if the former evaluates to true:

(when (launch-authorised? president)
  (fuel-missile icbm)
  (open-hatch silo)
  (launch-missile icbm))

Another example of control operator is the very useful and-let* macro, semi-standardised in SRFI-2, tests several expressions in sequence, aborting the computation if any of them evaluates to false, optionally binding variables to the expressions’s resulting values in nested scopes otherwise. If none of the expressions evaluate to false, the body is executed in the resulting lexical environment. Those familiar with Haskell may find it somewhat similar to the Maybe monad.

Actually several of Scheme’s own standard control operators are macros, implemented almost exactly as user-defined macros. A named let can be easily rewritten as a letrec operator:

;; a named let expression
(let collect ((seq (produce-list-of-numbers))
              (even '())
              (odd '()))
  (if (null? seq)
      (cons even odd)
      (let ((num (car seq)))
        (if (even? num)
            (collect (cdr seq) (cons num even) odd)
            (collect (cdr seq) even (cons num odd))))))
 
;; becomes a letrec expression
(letrec ((collect (lambda (seq even odd)
                    (if (null? seq)
                        (cons even odd)
                        (let ((num (car seq)))
                         (if (even? num)
                             (collect (cdr seq) (cons num even) odd)
                             (collect (cdr seq) even (cons num odd))))))))
  (collect (produce-list-of-numbers) '() '()))

Macros are of course not limited to adding new control operators to a language. One can design his own little language for solving specific problems, like stocks trading. Or a data modelling language, a graphics drawing language etc. A language for object-oriented programming such as Meroon can be seamlessly embedded within Scheme. Paul Graham’s On Lisp book on advanced Common Lisp programming has several chapters devoted to macros. In short, the developer becomes almost as powerful as the language designer, and such distinction is blurred. Armed with such power, the developer becomes much more productive and, therefore, happy.

Unfortunately not many programming languages provide good macro facilities. The C preprocessor, for instance, only does textual replacement and is no aware of expressions. Granted, the syntax of the programming languages of the Lisp family is very simple and uniform, making better macros possible. Nevertheless other languages like Haskell and OCaml with more complex syntax also provide such facilities.

3 Comments

  1. Ian says:

    Macros are not really indispensable, aren’t they? I mean, proabably more than 95% of the world can live without it :) (including the c/c++ developers)

  2. @Ian

    Yes.

    In the same vein that 90%+ of the world’s programmers are Java/C# programmers and all they do is write CRUDs all day long. They are already happy enough with a simple “generate getters and setters” from their favorite IDE. They don’t feel much need in the way of syntactic abstractions.

    But given the popularity of Ruby DSLs, one would think there is a demand for macros in mainstream languages. Not that Ruby offer real DSLs, but they fool people well enough. Not many people outside the Ruby community use them, however.

    In any case, this article is not intended for mediocre programmers, who wouldn’t even be able to understand it in the first place. It is intended for everyone else: people who already know they need macros, and people who haven’t yet figured out they need them.

  3. PhiLho says:

    Funny, I find this article while I investigate how macros are coded in various languages. Seed7 heavily relies on them (most of the base language is made out of system macros), Nermele seems to have a nice, powerful design, Nimrod has them too.
    A language like Scala has no macros, but it is expressive enough (with dot-less expressions, operators as functions, anonymous functions, etc.) to allow some clever DSL.

Leave a Reply