[racket] macro calling itself

From: Matthew Butterick (mb at mbtype.com)
Date: Mon Oct 27 16:10:39 EDT 2014

I'm unclear why I'm getting an unbound-identifier error in this macro. I've written other self-referencing macros in the past that have worked, so I'm unclear why this one doesn't. 

What I expect is that when `definer` is called with one argument, the first branch of `syntax-case` will stick on a default argument and call `definer` again. Which it does, except that in that case, it only binds the name 'id' rather than both 'id' and 'arg-id'. 


#lang racket
(require (for-syntax racket/syntax) rackunit)

(define-syntax (definer stx)
  (syntax-case stx ()
    [(_ id)
     #'(definer id zam)]
    [(_ id arg)
     (with-syntax ([arg-id (format-id stx "~a-~a" #'arg #'id)])
       #'(begin
           (define id (quote id))
           (define arg-id (quote arg-id))))]))


(definer foo-id bar)
(check-equal? foo-id 'foo-id)
(check-equal? bar-foo-id 'bar-foo-id)

(definer baz-id)
(check-equal? baz-id 'baz-id)
(check-equal? zam-baz-id 'zam-baz-id) ;; unbound identifier error


OTOH, this version of the macro, which repeats the expansion template rather than calling itself, works fine:


#lang racket
(require (for-syntax racket/syntax) rackunit)

(define-syntax (definer2 stx)
  (syntax-case stx ()
    [(_ id)
     (with-syntax ([arg-id (format-id stx "~a-~a" (format-id stx "zam") #'id)])
       #'(begin
           (define id (quote id))
           (define arg-id (quote arg-id))))]
    [(_ id arg)
     (with-syntax ([arg-id (format-id stx "~a-~a" #'arg #'id)])
       #'(begin
           (define id (quote id))
           (define arg-id (quote arg-id))))]))


(definer2 foo-id bar)
(check-equal? foo-id 'foo-id)
(check-equal? bar-foo-id 'bar-foo-id)

(definer2 baz-id)
(check-equal? baz-id 'baz-id)
(check-equal? zam-baz-id 'zam-baz-id) ;; no error this time

Posted on the users mailing list.