cisco / ChezScheme

Chez Scheme
Apache License 2.0
6.97k stars 985 forks source link

`eval-when` doesn't work under `module` or `library` #878

Open rvs314 opened 1 day ago

rvs314 commented 1 day ago

The eval-when page in CSUG gives an example of how to use eval-when in order to make a definition available in multiple phases:

(eval-when (compile load eval)
  (define nodups?
    (lambda (ids)
      (define bound-id-member?
        (lambda (id ids)
          (and (not (null? ids))
               (or (bound-identifier=? id (car ids))
                   (bound-id-member? id (cdr ids))))))
      (or (null? ids)
          (and (not (bound-id-member? (car ids) (cdr ids)))
               (nodups? (cdr ids)))))))

(define-syntax mvlet
  (lambda (x)
    (syntax-case x ()
      [(_ ((x ...) expr) b1 b2 ...)
       (and (andmap identifier? #'(x ...))
            (nodups? #'(x ...)))
       #'(call-with-values
           (lambda () expr)
           (lambda (x ...) b1 b2 ...))])))

(mvlet ((a b c) (values 1 2 3))
  (list (* a a) (* b b) (* c c)))

This works when put directly into a file, but doesn't work when inside of a library or module form:

(module test ()
  (eval-when (compile load eval)
    (define nodups?
      (lambda (ids)
        (define bound-id-member?
          (lambda (id ids)
            (and (not (null? ids))
                 (or (bound-identifier=? id (car ids))
                     (bound-id-member? id (cdr ids))))))
        (or (null? ids)
            (and (not (bound-id-member? (car ids) (cdr ids)))
                 (nodups? (cdr ids)))))))

  (define-syntax mvlet
    (lambda (x)
      (syntax-case x ()
        [(_ ((x ...) expr) b1 b2 ...)
         (and (andmap identifier? #'(x ...))
              (nodups? #'(x ...)))
         #'(call-with-values
               (lambda () expr)
             (lambda (x ...) b1 b2 ...))])))

  (mvlet ((a b c) (values 1 2 3))
         (list (* a a) (* b b) (* c c))))

Loading this causes the following error:

Exception: attempt to reference out-of-phase identifier nodups? at line 19, char 16 of /path/to/test.scm

The code does work if the definition of nodups? is wrapped in a meta keyword, but this doesn't let you use it at runtime. Is there a way to get around this? Is this the expected behavior? If so, can it be documented? If not, what is the issue? I'd be happy to help fix it if need be.

jltaylor-us commented 12 hours ago

Putting these definitions inside a library or module form makes them no longer top-level forms, which makes eval-when kinda useless.

The treatment of local expressions or definitions (those not at top level) that are wrapped in an eval-when depends only upon whether the situation eval is present in the list of situations. If the situation eval is present, the definitions and expressions are evaluated as if they were not wrapped in an eval-when form, i.e., the eval-when form is treated as a begin form. If the situation eval is not present, the forms are ignored; in a definition context, the eval-when form is treated as an empty begin, and in an expression context, the eval-when form is treated as a constant with an unspecified value.

The simplest way that I know of to provide some utility functions available at both expand and run time is to put them in their own library, separate from the one that uses them in syntax transformers.