[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Unhygienic macros



[Apologies for the verboseness...]

On Dec 10, Shriram Krishnamurthi wrote:
> Given ARC-IF from my previous message, consider ARC-COND:
> 
>   (define-syntax (arc-cond expr)
>     (syntax-case expr ()
>       [(_)
>        (syntax (void))]
>       [(_ q0 a0 rest ...)
>        (syntax (arc-if q0
>                        a0
>                        (arc-cond rest ...)))]))

I tried it with `arc-and' which I think is a bit more useful
(unrelated to if it exists in Arc or not)...

| (define-syntax (arc-and expr)
|   (syntax-case expr ()
|     [(_) (syntax #t)]
|     [(_ e) (syntax e)]
|     [(_ e rest ...)
|      (syntax (arc-if e (arc-and rest ...) #f))]))

Anyway, I couldn't figure out why this using the bogus definition of
`arc-if' failed.  To catch the problem I expanded things manually, and
then it is easy to see that a standalone (arc-if 1 it #f) works fine,
but the expansion of the two `arc-and's in (arc-and 1 it) gives you
(arc-if 1 it #f) but because this whole thing binds `it', then the
literal `it' there must be something different which can be verified
by trying (let ((it 2)) (arc-and 1 it)).

Now the real question is how can anyone internalize this information
to be able to catch such a bug...  Another thing that I thought would
be good to try out is the `define-macro' compatibility thing that
Lauri wrote recently, and that is already in "defmacro.ss":

  (define-syntax (define-macro stx)
    (syntax-case stx ()
     ((_ (macro . args) . body)
      (syntax (define-macro macro (lambda args . body))))
     ((_ macro transformer)
      (syntax
       (define-syntax (macro stx2)
         (let ((v (syntax-object->datum stx2)))
           (datum->syntax-object
            stx2
            (apply transformer (cdr v)))))))))

Using this,

  (define-macro (arc-if test true false)
    (let ((x (gensym)))
      `(let ((it ,test))
         (if it ,true ,false))))
  (define-macro (arc-and . args)
    (cond ((null? args) #t)
          ((null? (cdr args)) (car args))
          (else `(arc-if ,(car args) (arc-and ,@(cdr args)) #f))))

seems to be working fine.  But if `stx2' is replaced with `_' in the
`datum->syntax-object' call, then the result is "reference to
undefined identifier: define-macro", and if `(quote-syntax here)' is
used, then the result is a "reference to undefined identifier: it".
Now, I think I can swallow the second error -- all expansions have the
same binding position which forces using a different `it' every time.
But the first mistake is beyond me -- shouldn't there be a single
`define-macro' in any case, even if there is a new identifier bound by
it?


Something related -- I had another shot at the `unsyntax' thing.  The
first thing I tried was to see if I can create a context that renames
macros.  The following seems to be working fine:

| (define-syntax (hack stx)
|   (syntax-case stx ()
|     ((_ x) (syntax (fluid-let-syntax
|                        ((quote (lambda (stx)
|                                  (syntax-case stx ()
|                                    ((_ y) (syntax (+ y 1)))))))
|                      x)))))

But again, I'm worried about that `fluid-let-syntax' -- if it works by
some side-effect, wouldn't there be a problem when some code involves
calling `eval' on parallel threads?  I've tried this:

  (thread (lambda () (printf "--> ~s~%" (eval '(hack '1)))))

with a `sleep' inside the `fluid-let-syntax' and it looks like while
the thread is running things are working OK, but I'm not completely
convinced it is ok, probably not until I'll know how the temporary
change is done...


Anyway, the next step is to see if I can write a 2nd-level macro.
Doing this I ran into a problem that I couldn't figure out what causes
it:

  (module syntax-hack mzscheme
    (define-syntax (q stx)
      (syntax-case stx ()
        ((_ x) (syntax (syntax x)))))
    (provide q))
  (require-for-syntax syntax-hack)
  (define-syntax (foo stx)
    (syntax-case stx ()
      ((_ x) (q (+ x 1)))))
  (printf "--> ~s~%" (foo 12))

works fine -- `q' behaves like `syntax'.  But when I try to
destructure the input:

  (module syntax-hack mzscheme
    (define-syntax (q stx)
      (syntax-case stx ()
        ((_ (x ...)) (syntax (syntax (x ...))))))
    (provide q))

I get:

  ./foo.ss:101:20: compile: bad syntax; function application is not
  allowed, because no #%app syntax transformer is bound in: (+ 12 1)

What am I doing wrong?

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!