ChezScheme icon indicating copy to clipboard operation
ChezScheme copied to clipboard

Library isn't evaluated when syntax is used from it.

Open swatson555 opened this issue 3 years ago • 8 comments

If a library exports syntax and, that syntax is used from the library nothing in the library is evaluated which is problematic for library exports which rely on side-effects.

(library (n)
  (export s a)
  (import (chezscheme))
  (define on-load
    (begin (display "test")
           (newline)))
  (define a 0)
  (define-syntax s
    (syntax-rules ()
      ((s a b c)
       '(c b a)))))
Chez Scheme Version 9.5.7.7
Copyright 1984-2021 Cisco Systems, Inc.

> (import (n))
> (s 1 2 3)
(3 2 1)
> a
test
0
> 

The workaround I've been using for this is to force syntax to evaluate something in the library.

(library (n)
  (export s a)
  (import (chezscheme))
  (define on-load
    (begin (display "test")
           (newline)))
  (define a 0)
  (define-syntax s
    (syntax-rules ()
      ((s a b c)
       (begin on-load '(c b a))))))

This produces the expected result.

swatson555 avatar May 09 '22 01:05 swatson555

This behavior is by design. Accessing a compile-time value in a library does not invoke the run-time initialization of that library. To do otherwise would result in behavior that most people would find surprising. Similarly, if the output of a syntax transformer always included a reference to the library where the transformer was defined, even if the output did not otherwise reference anything from the library, most people would be surprised that they have a run-time dependency on a library only used at compile time.

jltaylor-us avatar May 18 '22 01:05 jltaylor-us

It's actually surprising to see that the default behavior is to defer evaluation of imports until an exported variable is evaluated from it. It's antithetical to most things in scheme and, chez is one of the only implementations I've seen that perform this aggressive optimization as default semantics.

I also agree that this isn't really a bug either especially if r6rs doesn't specify whether it's semantics should be one way or the other.

swatson555 avatar May 18 '22 02:05 swatson555

I looked at section 7.2 of r6rs and, it's stating that a library evaluates it's define expressions at phase 0 (runtime) and, it's syntax at phase 1 (expand time).

The r6rs seems inconsistent. It implies that the way chez does things are correct and, import expressions are meant to be deferred but, phases are meant to happen ordinarily. It also seems to be stating that phase 1 can happen before phase 0.

swatson555 avatar May 18 '22 03:05 swatson555

Maybe part of the confusion is expecting expressions entered in the repl to behave the same as if you were running a program (e.g., via top-level-program). While import is an expression in Chez, it's not really an expression in r6rs, it's a clause in the syntax of library and the not-really-defined structure of top-level programs. Unlike the repl, a library or program can be considered as a whole, and bindings from imported libraries are either used (in a phase) or they are not. (That doesn't help with the explanation of why referencing exported syntax doesn't cause a library's definitions to be evaluated, but it does address the idea that imports are "deferred". That behavior is specific to the repl.)

jltaylor-us avatar May 18 '22 03:05 jltaylor-us

According to R6RS Section 7.2

If any of a library’s definitions are referenced at phase 0 in the expanded form of a program, then an instance of the referenced library is created for phase 0 before the program’s definitions and expressions are evaluated.

An instance of a library is described earlier as the evaluation of its variable definitions and expressions.

Thus, the R6RS trigger for evaluating the variable definitions and expressions of a library is exactly what it is in Chez Scheme.

Jamie is right about why the instantiation appears to be deferred in the REPL. It occurs just before the evaluation of the first form whose expanded form contains a reference to one of the library's exports. Prior to that Chez Scheme has no way of knowing whether instantiation should occur.

dybvig avatar May 18 '22 16:05 dybvig

I just noticed something similar. I was about to report it as a bug.

This works in the REPL:

> (define-syntax my-macro (syntax-rules (<-) [(_ (<- x)) x]))
> (my-macro (<- 5))
5

This does not work:

my-library.scm

(library (my-library)
  (export my-macro)
  (import (chezscheme))
  (define-syntax my-macro (syntax-rules (<-) [(_ (<- x)) x])))

REPL

> (import (my-library))
> (my-macro (<- 5))
Exception: invalid syntax (my-macro (<- 5))
Type (debug) to enter the debugger.

It works if I don't have the scoped keyword <-. Is this also by design?

bjornkihlberg avatar Jun 10 '22 01:06 bjornkihlberg

This is by design. Please see this example in the Chez Scheme User's Guide, which shows how to fix your library.

owaddell avatar Jun 10 '22 16:06 owaddell

This is by design. Please see this example in the Chez Scheme User's Guide, which shows how to fix your library.

Thank you for the help and the swift response!

bjornkihlberg avatar Jun 10 '22 23:06 bjornkihlberg