views:

676

answers:

10

Many examples of macros seem to be about hiding lambdas, e.g. with-open-file in CL. I'm looking for some more exotic uses of macros, particularly in PLT Scheme. I'd like to get a feel for when to consider using a macro vs. using functions.

+4  A: 

I'll start answering the last question. When to use a macro instead of a function. The macros do things the functions can't, and the functions do thing the macros can't, so it'll be hard to mix them, but let's go deeper.

You use functions when you want the arguments evaluated and macros when you want the arguments un-evaluated. That's not very useful, is it? You use macros when you want to write something in a different way, when you see a pattern and you want to abstract. For example: I define three functions called foo-create, foo-process and foo-destroy for different values of foo and with similar bodies where the only change is foo. There's a pattern but a too-high level for a function, so you create a macro.

In my humble experience, macros in Scheme are to used as much as in other Lisps, like Common Lisp or Clojure. I suppose it's proof that maybe hygienic macros are not such a good idea, and here I would disagree with Paul Graham about why. It's not because sometimes you want to be dirty (un-hygienic) but because hygienic macros end up being complex or convoluted.

J. Pablo Fernández
"because hygienic macros end up being complex or convoluted" -- I don't think this is because of hygiene per se; it's more likely because Scheme hygienic macros are "high-level" and must be written in a language other than Scheme (which so far as I am concerned, is much less convinient than Scheme itself!)That's why I love Common Lisp and still use define-macro in Scheme, even though R5RS has this awckward syntax-rules thing... :-)
Jay
+3  A: 

Practical Common Lisp, by Peter Seibel, has a good introduction to macros. On Lisp, by Paul Graham, might be a good source of more complicated examples. Also, have look at the built-in macros in, say, Common Lisp.

Luís Oliveira
Scheme macros != Common Lisp macros
John the Statistician
+6  A: 

I only use Scheme macros (define-syntax) for tiny things like better lambda syntax:

(define-syntax [: x]
  (syntax-case x ()
    ([src-: e es ...]
     (syntax-case (datum->syntax-object #'src-: '_) ()
       (_ #'(lambda (_) (e es ...)))))))

Which lets you write

[: / _ 2]  ; <-- much better than (lambda (x) (/ x 2))

Dan Friedman has a mind-bending implementation of OO using macros: http://www.cs.indiana.edu/~dfried/ooo.pdf

But honestly, all the useful macros I've defined are stolen from Paul Graham's On Lisp and are generally easier to write with defmacro (define-macro in PLT Scheme). For example, aif is pretty ugly with define-syntax.

(define-syntax (aif x)
  (syntax-case x ()
    [(src-aif test then else)
     (syntax-case (datum->syntax-object (syntax src-aif) '_) ()
       [_ (syntax (let ([_ test]) (if (and _ (not (null? _))) then else)))])]))

define-syntax is odd in that it's only easy to use for very simple macros, where you are glad of the inability to capture variables; and very complicated macro DSLs, where you are glad of the inability to capture variables easily. In the first case you want to write the code without thinking about it, and in the second case you have thought enough about the DSL that you are willing to write part of it in the syntax-rules/syntax-case language which is not Scheme in order to avoid mystifying bugs.


But I don't use macros that much in Scheme. Idiomatic Scheme is so functional that many times you just want to write a functional program and then hide a few lambdas. I got on the functional train and now believe that if you have a lazy language or a good syntax for lambda, even that isn't necessary, so macros are not all that useful in a purely functional style.

So I'd recommend Practical Common Lisp and On Lisp. If you want to use PLT Scheme, I think most of their defmacro macros will work with define-macro. Or just use Common Lisp.

Nathan Sanders
+1  A: 

An example of a more advanced macro that is not a lambda form in disguise is Common Lisp's macro with-slots, which makes object slot access look like ordinary variable access:

(with-slots (state door) car
  (when (eq state :stopped)
    (setq state :driving-around)
    (setq door :closed)))

Note that this is not the same as binding the slot values to local variables and accessing those, as with-slots allows you to alter slots by way of SETQ and see external changes immediately.

Matthias Benkard
The use of setq really is deprecated in favor of setf. It's less confusing for new (common) lispers if examples like this use setf.
simon
+5  A: 

Macros are needed in order to implement new control structures and new binding constructs.

Thus look for these kinds of constructs at http://planet.plt-scheme.org. At PLaneT you both browse the documentation and the code.

Examples for new control structures:

http://planet.plt-scheme.org/package-source/soegaard/control.plt/2/0/planet-docs/manual/index.html

To find examples of new binding forms, look for macros that begin with "with-". One useful example is found in math.plt also from PLaneT.

  ; Within a (with-modulus n form1 ...) the return values of
  ; the arithmetival operations +, -, * and ^ are automatically
  ; reduced modulo n. Furthermore (mod x)=(modulo x n) and
  ; (inv x)=(inverse x n).

  ; Example: (with-modulus 3 (^ 2 4)) ==> 1

  (define-syntax (with-modulus stx)
    (syntax-case stx ()
      [(with-modulus e form ...)
       (with-syntax ([+   (datum->syntax-object (syntax with-modulus) '+)]
                     [-   (datum->syntax-object (syntax with-modulus) '-)]
                     [*   (datum->syntax-object (syntax with-modulus) '*)]
                     [^   (datum->syntax-object (syntax with-modulus) '^)]
                     [mod (datum->syntax-object (syntax with-modulus) 'mod)]
                     [inv (datum->syntax-object (syntax with-modulus) 'inv)])
         (syntax (let* ([n e]
                        [mod    (lambda (x)   (modulo x n))]
                        [inv    (lambda (x)   (inverse x n))]
                        [+      (compose mod +)]
                        [-      (compose mod -)]
                        [*      (compose mod *)]
                        [square (lambda (x) (* x x))]
                        [^      (rec ^ (lambda (a b)
                                         (cond
                                           [(= b 0)   1]
                                           [(even? b) (square (^ a (/ b 2)))]
                                           [else      (* a (^ a (sub1 b)))])))])
                   form ...)))]))
soegaard
+1  A: 

The Automata via Macros paper presents a functional programming pearl on implementing finite state machines via macros in Scheme.

The book The Reasoned Schemer ends with a full macro-based implementation of miniKanren, the logic programming language used in the book. This paper presents miniKanren and its implementation more formally and concisely than in the book.

namin
A: 

I use them when procedures do not suffice.

leppie
+1  A: 

I had a curry macro when I used to do a lot of scheme on my palm. It was pretty handy.

Dustin
+1  A: 

Scheme macros let you add features that the original language authors did not include themselves; that is the whole philosophy behind macros.

Here is a tiny example: PLT Scheme provides a language for writing presentations called Slideshow. I used macros to associate a slide number with a slide so that I could more easily manage them.

grettke
+1  A: 

I’ve written a macro that provides infix syntax. Nothing too fancy; no precedence. While I’m generally fine with prefix syntax, I prefer infix for < and >.

Robert Fisher