[plt-scheme] moving macros from top-level to module-level

From: Felix Klock's plt proxy (pltscheme at pnkfx.org)
Date: Sat Jul 17 13:23:01 EDT 2004

Hi ho PLTers-

I'm trying to do some macro hackery, and I developed a macro that has a 
desired behavior at the top level, but ceases to retain that behavior 
after being shifted into its own module and then provided from that 
module.

I have a rough understanding of what MzScheme is doing and why things 
are failing, but I don't know how to make the provided DEFINE-LAZY act 
like it does at the top level.  The standard tricks (like giving up 
automatic hygiene and using DATUM->SYNTAX-OBJECT to produce the desired 
forms in the "right" namespace) do not seem to be working.

Any advice?

-Felix

p.s.
Here is the code at the top-level  Also, after the top-level code 
(which "works"), I've included the module that has three attempts to 
provide DEFINE-LAZY, all of which cause failures (for the first, the 
DEFINE-LAZY form itself fails, while for the other two, the usage of 
the macros/procedures defined by DEFINE-LAZY form is what fails).

p.p.s
As an aside, I think DEFINE-LAZY demonstrates an interesting usage of 
SYNTAX-ID-RULES that does not use the SET! feature of that macro 
definition form.

----------------------- TOP LEVEL -----------------------

(define-syntax define-lazy
   (syntax-rules ()
     [(define-lazy (OP ARG ...) BODY ...)
      (begin

        (define (function ARG ...)
          (let-syntax
              ((ARG (syntax-id-rules ()
                      [(_ RANDS (... ...))
                       ((force ARG) RANDS (... ...))]
                      [_
                       (force ARG)]))
               ...)
            BODY ...))

        (define-syntax OP
          (syntax-rules ()
            [(OP ARG ...)
             (function (delay ARG) ...)])))]
     ))

;; Sample usage:
;; (new-if #t 1 (/ 1 0)) ==> 1
(define-lazy (new-if test then else)
   (if test then else))
(new-if #t 1 (/ 1 0))

----------------------- MODULE LEVEL -----------------------

(module lazy mzscheme
   (provide define-lazy/1 define-lazy/2 define-lazy/3)

   ;; I'm trying to run these test cases on the base form:
   #|
   (begin
     (require (lib "lazy.scm" "util"))
     (define-lazy/3 (stream-cons x y) (cons x (delay y)))
     (stream-cons 1 2))
   |#

   ;; Note that the first form, DEFINE-LAZY/1 works if you insert it at
   ;; the top level (at least for doing experiments at the top-level)
   ;; while the second form, DEFINE-LAZY/2, fails in the same manner at
   ;; the top-level that it does when provided from a module

   ;; Attempt One, fails with:
   ;; define-values: identifier for a top-level definition already has a 
module context at: function in: (define-values (function) (lambda (x y) 
(let-syntax ((x (syntax-id-rules () ((_ rands ...) ((forc...
   (define-syntax define-lazy/1
     (syntax-rules ()
       [(define-lazy (OP ARG ...) BODY ...)
        (begin

          (define (function ARG ...)
            (let-syntax
                ((ARG (syntax-id-rules ()
                                       [(_ RANDS (... ...))
                                        ((force ARG) RANDS (... ...))]
                                       [_
                                        (force ARG)]))
                 ...)
              BODY ...))

          (define-syntax OP
            (syntax-rules ()
              [(OP ARG ...)
               (function (delay ARG) ...)])))]
       ))

;; Attempt Two, fails with:
;; compile: bad syntax; reference to top-level identifiers is not 
allowed, because no #%top syntax transformer is bound in: function1
   (define-syntax define-lazy/2
     (lambda (stx)
       (syntax-case stx ()
         [(define-lazy (OP ARG ...) BODY ...)
          (with-syntax ((function (generate-temporaries '(function))))
            #`(begin
                (define (function ARG ...)
                  (let-syntax
                      ((ARG (syntax-id-rules ()
                              [(_ RANDS (... ...))
                               ((force ARG) RANDS (... ...))]
                              [_
                               (force ARG)]))
                       ...)
                    BODY ...))

                (define-syntax OP
                  (syntax-rules ()
                    [(OP ARG ...)
                     (function (delay ARG) ...)]))))]
         )))

   ;; Attempt Three, fails with:
   ;; compile: bad syntax; reference to top-level identifiers is not 
allowed, because no #%top syntax transformer is bound in: function1
   (define-syntax define-lazy/3
     (lambda (stx)
       (syntax-case stx ()
         [(define-lazy (OP ARG ...) BODY ...)
          (datum->syntax-object
           #'OP
           (let ((function (car (generate-temporaries '(function)))))
             `(begin
                (define ,(cons function (syntax-e #'(ARG ...)))
                  ,(cons
                    'let-syntax
                    (cons (map (lambda (arg-stx)
                                 `(,arg-stx
                                   (syntax-id-rules ()
                                     [(_ RANDS (... ...))
                                      ((force ,arg-stx) RANDS (... ...))]
                                     [_
                                      (force ,arg-stx)])))
                               (syntax-e #'(ARG ...)))
                          (syntax-e #'(BODY ...)))))

                (define-syntax ,#'OP
                  (syntax-rules ()
                    [(,#'OP ,@(syntax-e #'(ARG ...)))
                     (,function ,@(map (lambda (arg-stx)
                                         `(delay ,arg-stx))
                                       (syntax-e #'(ARG ...))))])))))]
          )))
   )

----
"There she is officers: the woman
who programmed me for evil!"  -Bender



Posted on the users mailing list.