Anaphoric Macros

Does the convenience that anaphoric macros provide justify breaking hygiene? In that chapter of On Lisp, the author stated that:

This chapter will show that variable capture can also be used constructively. There are some useful macros which couldn’t be written without it.

My evaluation of that claim is that while the former is true, anaphoric macros are not evidence of such a case as they only save you a variable binding. The latter claim is interesting because it begs the question of whether or not they should be written as macros at all. It made me wonder how anaphoric macros might look in Scheme, how they might look as functions, and whether one is clearly superior to the other.

I wrote two versions of aif, the first is a macro (m-aif) and the second is a function (f-aif). Both provide the same functionality albeit with a different mechanism for implementation and syntax for application:

m-aif

(define-syntax m-aif
  (λ (stx)
    (syntax-case stx ()
      ((_ test-form then-form)
       (syntax (_ test-form then-form void)))
      ((_ test-form then-form else-form)
       (with-syntax ((it (syntax-local-introduce (syntax it))))
         (syntax
          (let ((it test-form))
            (if it
                then-form
                else-form))))))))

f-aif

(define f-aif
  (λ (test-form then-form . else-form)
    (let ((it (test-form)))
      (if it
          (then-form it)
          (unless (null? else-form)
            ((car else-form)))))))

The difference between utilizing the two versions becomes a matter of syntactic preference:

m-aif

(m-aif (identity 10)
       (display (* it 23)))

f-aif

(f-aif (λ () (identity 10))
       (λ (it) (display (* it 23))))

Which do you like better?

It should be an easy decision; with f-aif you get the desired functionality without breaking hygiene, albeit at the “cost” forcing the user to utilize lambda expressions.

In practice, requiring the use of lambda expressions is not an egregious demand of the user; instead, it is a pleasure. lambda is a special thing. The functionality that it provides is often more than enough to implement functionality that may initially appear to require macros (hygienic or not).

Macros are often times clearly the only, and the best, solution. This time isn’t one of them.

ADDENDUM: 03/04/11:

Jim asked:

If you are concerned about variable capture then why don’t you write an anaphoric if macro that takes the variable name to introduce as an argument?

Answer: When I wrote this I still didn’t grok hygiene.

Here is what I mean (and think Jim means):

#!r6rs

(import (rnrs))

(define-syntax aif*
  (syntax-rules ()
    ((_ it test con . alt)
     (let ((it test))
       (if it con . alt)))))

(define-syntax stomp?
  (syntax-rules ()
    ((_ body)
     (let ((foo 0))
       (begin
         body
         (display "Stomp's foo: ")
         (display foo)(newline))))))

(aif* foo (+ 10) 
      (stomp? 
       (begin (display "My foo: ")
              (display foo)(newline))))

What do you think would get printed out here? At first glance you might think that stomp? will clobber foo and the user will not get the value they had expected, something like this:

My foo: 0
Stomp's foo: 0

Here is the output though:

My foo: 10
Stomp's foo: 0

Surprised? syntax-rules retains the correct lexical binding so the user’s code and the macro’s code both referred to the correct binding of foo (foo_user==10 vs. foo_{stomp macro}==0). This is the benefit of “hygeine”. Looking back, I’m not sure why hygiene didn’t jump out as an extremely simple concept: macro bodies retain their lexical bindings.

Great question. Thanks for posting even after 3 years!


4 thoughts on “Anaphoric Macros”

  1. You’re missing the point of macros entirely. They are useful for abstracting syntax. m-aif does so, f-aif doesn’t. One is not better than the other – it depends on what you want to write and what you’re willing to write.

  2. IF can also be written as a function taking thunks, but that would be silly.

    If you are concerned about variable capture then why don’t you write an anaphoric if macro that takes the variable name to introduce as an argument?

    (define-syntax aif*
    (syntax-rules ()
    ((_ it test con . alt)
    (let ((it test))
    (if it con . alt)))))

    Also this post is 3 years old, why are we commenting on it

  3. ZB:

    When I wrote this I still didn’t grok hygiene. I updated the post in response. Thank you for posting even after 3 years! :)

Leave a Reply

Your email address will not be published. Required fields are marked *