bug-guile
[Top][All Lists]
Advanced

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

Re: Bug with macros in SCM and Guile


From: Aubrey Jaffer
Subject: Re: Bug with macros in SCM and Guile
Date: Sat, 6 Jan 2007 22:42:22 -0500 (EST)

 | From: Marc Feeley <address@hidden>
 | Date: Wed, 29 Nov 2006 15:58:57 -0500
 | 
 | -----BEGIN PGP SIGNED MESSAGE-----
 | Hash: SHA1
 | 
 | Dear Aubrey and Guile developers, I have found a nasty bug in SCM's  
 | and Guile's macro expansion algorithm.

Which specification does the behavior violate?

 | The code that reproduces the bug is attached below.  This example
 | is an extremely condensed fragment of a portable namespace
 | management system I am developing (it is currently working in 10
 | other implementations of Scheme and I would like to add SCM and
 | Guile to that list).  The example contains 2 definitions for the
 | macro foo, and macro calls to foo between the two definitions.  The
 | problem is that the first definition is not always the one that is
 | used to expand the macro calls.  I expected the expansion to be
 | done sequentially (top-down) using the current definition of the
 | macro foo (in this case always the first definition).
 | 
 | For SCM it seems that a macro call is normally expanded when an  
 | expression is encountered in a top-down traversal of the code, but  
 | not always.  For example, in the function bar2 below the macro call  
 | to foo is expanded when bar2 is *called*, and at that point a new  
 | definition of the macro foo has been introduced.
 | 
 | Apparently Guile consistently expands macro calls when they are  
 | evaluated (only the first time I suppose).  In the example, this  
 | leads to the strange consequence that bar1 and bar3 behave  
 | differently even though they are defined the same.
 | 
 | Note that in my real code, the code is split into 2 files and each  
 | file only defines the macro once.  Imagine in the code below that  
 | file1 contains the code up to but not including the second defmacro,  
 | and file2 contains the code starting at the second defmacro.
 | 
 | Perhaps it is too difficult to fix this bug.  However I would be  
 | quite happy with a special form to force macro expansion, i.e.  
 | something like
 | 
 |    (define f
 |      (fully-expand-now
 |        (lambda (arg1 ...)
 |           ...)))
 | 
 | Is something like this possible?

Yes, using SLIB's DEFMACRO:EXPAND*
(http://swiss.csail.mit.edu/~jaffer/slib_3.html#SEC23)

  (require 'defmacroexpand)
  (slib:eval (defmacro:expand* '(begin
  (defmacro foo (x)
     (display "expanding-foo-version1\n")
     `(list 'foo-v1 ',x))

  (define (bar1)    (foo in-bar1))
  (define (bar2) #f (foo in-bar2))
  (define (bar3)    (foo in-bar3))

  (write (bar1)) (newline)

  (defmacro foo (x)
     (display "expanding-foo-version2\n")
     `(list 'foo-v2 ',x))

  (write (bar2)) (newline)
  (write (bar3)) (newline)
  )))

SCM-5e3 Prints:

  expanding-foo-version2
  expanding-foo-version2
  expanding-foo-version2
  (foo-v2 in-bar1)
  (foo-v2 in-bar2)
  (foo-v2 in-bar3)

But Guile-1.6.7 Prints:

  expanding-foo-version1
  (foo-v1 in-bar1)
  expanding-foo-version2
  (foo-v2 in-bar2)
  expanding-foo-version2
  (foo-v2 in-bar3)

Hmm...  Lets look at how the program is transformed:

  (require 'pretty-print)
  (pretty-print (defmacro:expand* '(begin
  (defmacro foo (x)
     (display "expanding-foo-version1\n")
     `(list 'foo-v1 ',x))

  (define (bar1)    (foo in-bar1))
  (define (bar2) #f (foo in-bar2))
  (define (bar3)    (foo in-bar3))

  (write (bar1)) (newline)

  (defmacro foo (x)
     (display "expanding-foo-version2\n")
     `(list 'foo-v2 ',x))

  (write (bar2)) (newline)
  (write (bar3)) (newline)
  )))

SCM-5e3 prints:

  (begin
    (defmacro:simple-defmacro
      foo
      scm:G20
      (let ((scm:G21 scm:G20))
        (let ((x (car scm:G21)))
          (display "expanding-foo-version1
  ")
          `(list 'foo-v1 ',x))))
    (define (bar1) (foo in-bar1))
    (define (bar2) #f (foo in-bar2))
    (define (bar3) (foo in-bar3))
    (write (bar1))
    (newline)
    (defmacro:simple-defmacro
      foo
      scm:G22
      (let ((scm:G23 scm:G22))
        (let ((x (car scm:G23)))
          (display "expanding-foo-version2
  ")
          `(list 'foo-v2 ',x))))
    (write (bar2))
    (newline)
    (write (bar3))
    (newline))

Guile-1.6.7 Prints:

  (begin
    (eval-case
      ((load-toplevel)
       (define foo
         (defmacro:transformer
           (lambda (x)
             (display "expanding-foo-version1
  ")
             `(list 'foo-v1 ',x)))))
      (else (error "defmacro can only be used at the top level")))
    (define (bar1) (foo in-bar1))
    (define (bar2) #f (foo in-bar2))
    (define (bar3) (foo in-bar3))
    (write (bar1))
    (newline)
    (eval-case
      ((load-toplevel)
       (define foo
         (defmacro:transformer
           (lambda (x)
             (display "expanding-foo-version2
  ")
             `(list 'foo-v2 ',x)))))
      (else (error "defmacro can only be used at the top level")))
    (write (bar2))
    (newline)
    (write (bar3))
    (newline))

So the difference seems to be in how DEFMACRO is implemented in the
two implementations.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]