Discussion:
[Larceny-users] Ticket 638
Lynn Winebarger
2009-04-21 02:04:20 UTC
Permalink
This ticket is pretty interesting:

% cat temp2.sps
(import (for (rnrs) run expand)
(rename (only (rnrs base) cons) (cons kons)))

(free-identifier=? #'cons #'kons)
% ./larceny --r6rs --program temp2.sps

Syntax violation: invalid reference

Attempt to use binding of cons in library (program~19XamJ~2) at
invalid level -1. Binding is only available at levels: 0 1 2

Evidently this should evaluate to #t according to the PLT test suite.

First, if it isn't clear, this is occuring because Andre's system is
resolving the reflected identifier in the syntax form, regardless of
whether the form is being evaluated on the right hand side of a syntax
binding form (define-syntax etc). That's why it reports the
identifier being desired at level -1.

There are multiple issues:
1) The syntax violation is getting triggered too early. There's an
echo of Will's complaint that R6RS requires compiling programs with
obvious errors to give error messages at run-time
2) The binding is getting resolved too early. It shouldn't be
resolved until it appears on the right hand side of a syntax binding
form. Consider whether the following program should produce 'a or 7:

(library (defines x) (export x) (import (rnrs))
(define x 7))

(library (defines foo)
(export foo)
(import (rnrs) (rnrs syntax-case) (for (defines x) (meta -1)))
(define foo (syntax x))
)

(library (expands foo)
(export foo-expander)
(import (for (rnrs) run expand)
(for (rnrs syntax-case) run expand)
(for (defines foo) run expand))

(define x 'a)
(define-syntax foo-expander
(lambda (x)
(syntax-case x ()
((_) foo))))
)

(import (expands foo))
(foo-expander)

This test would imply 'a is the right answer. That preserves
referential transparency at the site of the macro definiton, even
though the identifier object is constructed outside that context.

3) It doesn't make a lot of sense to have free-identifier=? if you
can't create a free identifier without triggering an expand time
error.

Lynn
Derick Eddington
2009-04-21 07:04:22 UTC
Permalink
Post by Lynn Winebarger
% cat temp2.sps
(import (for (rnrs) run expand)
(rename (only (rnrs base) cons) (cons kons)))
(free-identifier=? #'cons #'kons)
% ./larceny --r6rs --program temp2.sps
Syntax violation: invalid reference
Attempt to use binding of cons in library (program~19XamJ~2) at
invalid level -1. Binding is only available at levels: 0 1 2
Evidently this should evaluate to #t according to the PLT test suite.
For implicit phasing systems, such as Ikarus and Ypsilon, a result of #T
makes sense because any phase level specifications are ignored and
`cons' and `kons' are bound to the same thing at every phase.

But for explicit phasing systems, such as Larceny and PLT, I don't think
a result of #T makes sense because `cons' and `kons' are unbound at
phase -1 and unbound identifiers are compared according to their
spelling. If they are supposed to be bound, that's either implicit
phasing or confusing syntax template semantics.

I've encountered this weirdness with PLT syntax templates before. I
found it quite confusing and inconsistent that PLT's syntax templates'
identifiers are apparently not always (- phase 1) (even more explicit
phasing complications to scare people away from learning macros, yay!).
I think it's a bug in PLT because my code shown below has a syntax
template at phase 0 and the identifiers in it are certainly at phase -1
(because I made that the only possibility) and it works on PLT, which is
inconsistent with the PLT test suite program above.
Post by Lynn Winebarger
First, if it isn't clear, this is occuring because Andre's system is
resolving the reflected identifier in the syntax form, regardless of
whether the form is being evaluated on the right hand side of a syntax
binding form (define-syntax etc). That's why it reports the
identifier being desired at level -1.
I think Larceny's phase mismatch detection is very preferable to
allowing separate namespaces (that would be very unSchemely) for
different phases such that `cons' would be allowed unbound at phase -1
but bound at the phases it's imported for. And this detection is
consistent with the fact that R6RS requires preventing importing the
same name but different bindings at different phases.
Post by Lynn Winebarger
1) The syntax violation is getting triggered too early.
How can you tell?
Post by Lynn Winebarger
There's an
echo of Will's complaint that R6RS requires compiling programs with
obvious errors to give error messages at run-time
That temp2.sps is being compiled and run in the same process, so how can
you tell when the error is happening? I think it probably actually is
happening at expand-time.
Post by Lynn Winebarger
2) The binding is getting resolved too early. It shouldn't be
resolved until it appears on the right hand side of a syntax binding
Because Scheme is lexically scoped and maintaining that is a primary
purpose of R6RS syntax templates, it's obvious to me that `foo' should
be bound to a syntax object representing the identifier `x' from the
lexical scope where that identifier occurs, and that identifier refers
to the `x' imported from `(defines x)'.
Post by Lynn Winebarger
(library (defines x) (export x) (import (rnrs))
(define x 7))
(library (defines foo)
(export foo)
(import (rnrs) (rnrs syntax-case) (for (defines x) (meta -1)))
(define foo (syntax x))
)
(library (expands foo)
(export foo-expander)
(import (for (rnrs) run expand)
(for (rnrs syntax-case) run expand)
(for (defines foo) run expand))
(define x 'a)
(define-syntax foo-expander
(lambda (x)
(syntax-case x ()
((_) foo))))
)
(import (expands foo))
(foo-expander)
This test would imply 'a is the right answer.
Ikarus, Larceny, PLT, and Ypsilon (which all have different
implementations of R6RS macros) all say the answer is 7.
Post by Lynn Winebarger
That preserves
referential transparency at the site of the macro definiton, even
though the identifier object is constructed outside that context.
I don't think it would preserve referential transparency because the
value of `foo' is a syntax object representing the identifier `x' from
the lexical scope where that identifier occurred. Referential
transparency means an expression can be replaced with its value without
changing the semantics. If you replaced the `foo' expression with its
value, it would be like importing the `x' from `(defines x)' with a
renamed name and referring to that renamed name.

I certainly want to be able to make helpers like in the below example
and know what their output is going to refer to. (This is also the code
with the PLT syntax template phase level inconsistency I mentioned
above).

$ cat help/do-it.sls
#!r6rs
(library (help do-it)
(export do-it)
(import (rnrs))
(define (do-it x)
(list x x)))
$ cat help/helper.sls
#!r6rs
(library (help helper)
(export helper)
(import (rnrs)
(for (help do-it) (meta -1)))
(define (helper x)
(quasisyntax (do-it (unsyntax x)))))
$ cat use-help.sps
#!r6rs
(import (rnrs)
(for (help helper) expand))
(define do-it 'different)
(define-syntax m
(lambda (stx)
(syntax-case stx ()
((_ x) (helper (syntax x))))))
(write (m 1)) (newline)
$ ikarus --r6rs-script use-help.sps
(1 1)
$ larceny -path . -r6rs -program use-help.sps
(1 1)
$ plt-r6rs ++path . use-help.sps
(1 1)
$ ypsilon --sitelib . use-help.sps
(1 1)
$

IIUC, you're saying the syntax object returned from `helper' should
refer to the `do-it' in the program. That would defeat the whole point
of hygienic macros.
Post by Lynn Winebarger
3) It doesn't make a lot of sense to have free-identifier=? if you
can't create a free identifier without triggering an expand time
error.
You can do that. Larceny's error is happening because the free
identifiers have imported bindings in scope but not at the right phase
level and so it's preventing separate namespaces from existing.
--
: Derick
----------------------------------------------------------------
Lynn Winebarger
2009-04-21 11:10:43 UTC
Permalink
On Tue, Apr 21, 2009 at 3:04 AM, Derick Eddington
Post by Derick Eddington
[...]
But for explicit phasing systems, such as Larceny and PLT, I don't think
a result of #T makes sense because `cons' and `kons' are unbound at
phase -1 and unbound identifiers are compared according to their
spelling.  If they are supposed to be bound, that's either implicit
phasing or confusing syntax template semantics.
Right, PLT's explicit phasing is exactly why this is interesting. I
should have been clearer that the three points I offered are
hypothetical reasons for the inconsistency rather than expressions of
my preferences.
Post by Derick Eddington
I think Larceny's phase mismatch detection is very preferable to
allowing separate namespaces (that would be very unSchemely) for
different phases such that `cons' would be allowed unbound at phase -1
but bound at the phases it's imported for.  And this detection is
consistent with the fact that R6RS requires preventing importing the
same name but different bindings at different phases.
Ok, I hadn't noticed the latter restriction in R6RS.
Post by Derick Eddington
Post by Lynn Winebarger
1) The syntax violation is getting triggered too early.
How can you tell?
Post by Lynn Winebarger
There's an
echo of Will's complaint that R6RS requires compiling programs with
obvious errors to give error messages at run-time
That temp2.sps is being compiled and run in the same process, so how can
you tell when the error is happening?  I think it probably actually is
happening at expand-time.
Exactly - evidently the error should wait at least until it appears on
the right hand side of a macro definition. R6RS has a lot of
requirements like that - the compiler has to generate code that will
definitely signal an exception at run-time, even if it's provable at
compile time that the exception will be signaled.
Post by Derick Eddington
Post by Lynn Winebarger
2) The binding is getting resolved too early.  It shouldn't be
resolved until it appears on the right hand side of a syntax binding
Because Scheme is lexically scoped and maintaining that is a primary
purpose of R6RS syntax templates, it's obvious to me that `foo' should
be bound to a syntax object representing the identifier `x' from the
lexical scope where that identifier occurs, and that identifier refers
to the `x' imported from `(defines x)'.
The point of this hypothetical was whether it's the macro that's
hygeinic or the syntactic template. You're arguing for the template,
but I think it's arguable that the template does not become actual
source code until it's on the right hand side of a macro definition,
and so referential transparency should be "through" that macro
defintion, not the template.
Post by Derick Eddington
[...]
IIUC, you're saying the syntax object returned from `helper' should
refer to the `do-it' in the program.  That would defeat the whole point
of hygienic macros.
Again, you're referring to hygienic templates, not macros as such.
Post by Derick Eddington
Post by Lynn Winebarger
3) It doesn't make a lot of sense to have free-identifier=? if you
can't create a free identifier without triggering an expand time
error.
You can do that.  Larceny's error is happening because the free
identifiers have imported bindings in scope but not at the right phase
level and so it's preventing separate namespaces from existing.
Then this goes back to point 1, the error should be signaled either
when the template is actually appears in a macro defintion or when it
appears as part of source code as a result of the macro invocation.
Before that, maybe it should just be a warning. Why cause an error if
the offending identifier never even appears in the output of the
macro?

Lynn
Lynn Winebarger
2009-04-21 11:28:04 UTC
Permalink
Right.
[...] Why cause an error if the offending identifier never
even appears in the output of the macro?
You're questioning "why does the following raise an exception instead of
returning #t or #f like all predicates do?", right?
(import (rnrs))
;;; Autoloading ...
(free-identifier=? #'car #car)
Error: syntax-violation: invalid reference: Attempt to use binding of car in
library () at invalid level -1.  Binding is only available at levels: 0 1
car
Entering debugger; type "?" for help.
debug>
Aziz,,,
Abdulaziz Ghuloum
2009-04-21 11:26:00 UTC
Permalink
[...] Why cause an error if the offending identifier never
even appears in the output of the macro?
You're questioning "why does the following raise an exception instead
of returning #t or #f like all predicates do?", right?
(import (rnrs))
;;; Autoloading ...
(free-identifier=? #'car #car)
Error: syntax-violation: invalid reference: Attempt to use binding of
car in library () at invalid level -1. Binding is only available at
levels: 0 1 car
Entering debugger; type "?" for help.
debug>

Aziz,,,
Derick Eddington
2009-04-21 11:21:46 UTC
Permalink
Post by Derick Eddington
Post by Lynn Winebarger
% cat temp2.sps
(import (for (rnrs) run expand)
(rename (only (rnrs base) cons) (cons kons)))
(free-identifier=? #'cons #'kons)
% ./larceny --r6rs --program temp2.sps
Syntax violation: invalid reference
Attempt to use binding of cons in library (program~19XamJ~2) at
invalid level -1. Binding is only available at levels: 0 1 2
1) The syntax violation is getting triggered too early.
How can you tell?
Post by Lynn Winebarger
There's an
echo of Will's complaint that R6RS requires compiling programs with
obvious errors to give error messages at run-time
That temp2.sps is being compiled and run in the same process, so how can
you tell when the error is happening? I think it probably actually is
happening at expand-time.
I was wrong about that. I wonder why the error isn't detected at
expand-time. Does R6RS really require this type of error to be reported
at run-time? E.g.:

$ cat temp2.sls
(library (temp2)
(export)
(import (for (rnrs) run expand)
(rename (only (rnrs base) cons) (cons kons)))
(display "before\n")
(write (free-identifier=? #'cons #'kons)) (newline))

$ larceny -err5rs
Larceny v0.97a4 (alpha test) (Apr 19 2009 17:48:52, precise:Linux:unified)
larceny.heap, built on Sun Apr 19 17:50:33 PDT 2009
ERR5RS mode (no libraries have been imported)
Post by Derick Eddington
(import (primitives compile-stale-libraries))
(compile-stale-libraries)
Compiling temp2.sls
Autoloading (rnrs)
Autoloading (rnrs enums)
Autoloading (rnrs lists)
Autoloading (rnrs syntax-case)
Autoloading (rnrs conditions)
Autoloading (err5rs records procedural)
Autoloading (rnrs exceptions)
Autoloading (rnrs hashtables)
Autoloading (rnrs arithmetic bitwise)
Autoloading (rnrs programs)
Autoloading (rnrs files)
Autoloading (rnrs io ports)
Autoloading (larceny deprecated)
Autoloading (rnrs records syntactic)
Autoloading (rnrs records procedural)
Autoloading (rnrs control)
Autoloading (rnrs sorting)
Autoloading (rnrs bytevectors)
Autoloading (rnrs unicode)
$ larceny -path . -err5rs
Larceny v0.97a4 (alpha test) (Apr 19 2009 17:48:52, precise:Linux:unified)
larceny.heap, built on Sun Apr 19 17:50:33 PDT 2009
ERR5RS mode (no libraries have been imported)
Post by Derick Eddington
(import (temp2))
Autoloading (temp2)
Autoloading (rnrs)
Autoloading (rnrs enums)
Autoloading (rnrs lists)
Autoloading (rnrs syntax-case)
Autoloading (rnrs conditions)
Autoloading (err5rs records procedural)
Autoloading (rnrs exceptions)
Autoloading (rnrs hashtables)
Autoloading (rnrs arithmetic bitwise)
Autoloading (rnrs programs)
Autoloading (rnrs files)
Autoloading (rnrs io ports)
Autoloading (larceny deprecated)
Autoloading (rnrs records syntactic)
Autoloading (rnrs records procedural)
Autoloading (rnrs control)
Autoloading (rnrs sorting)
Autoloading (rnrs bytevectors)
Autoloading (rnrs unicode)
before

Syntax violation: invalid reference

Attempt to use binding of cons in library (temp2) at invalid level -1. Binding is only available at levels: 0 1 2

Form: cons

Trace:



Error: unhandled condition:
Compound condition has these components:
#<record &who>
who : "invalid reference"
#<record &message>
message : "Attempt to use binding of cons in library (temp2) at invalid level -1. Binding is only available at levels: 0 1 2"
#<record &syntax>
form : cons
subform : #f

Entering debugger; type "?" for help.
debug>
--
: Derick
----------------------------------------------------------------
Derick Eddington
2009-04-21 12:42:23 UTC
Permalink
Post by Derick Eddington
Post by Derick Eddington
Post by Lynn Winebarger
There's an
echo of Will's complaint that R6RS requires compiling programs with
obvious errors to give error messages at run-time
That temp2.sps is being compiled and run in the same process, so how can
you tell when the error is happening? I think it probably actually is
happening at expand-time.
I was wrong about that. I wonder why the error isn't detected at
expand-time. Does R6RS really require this type of error to be reported
at run-time?
R6RS Libraries 12.1 says:
"A reference to an identifier introduced into the output of a
transformer refers to [...] the closest enclosing lexical binding where
the identifier appears (within a syntax <template>) inside the
transformer body or one of the helpers it calls."

That "or one of the helpers it calls" suggests to me that templates in
helpers are also hygienic, regardless of where those helpers are from.
--
: Derick
----------------------------------------------------------------
Derick Eddington
2009-04-21 13:10:25 UTC
Permalink
Post by Derick Eddington
Post by Derick Eddington
Post by Derick Eddington
Post by Lynn Winebarger
There's an
echo of Will's complaint that R6RS requires compiling programs with
obvious errors to give error messages at run-time
That temp2.sps is being compiled and run in the same process, so how can
you tell when the error is happening? I think it probably actually is
happening at expand-time.
I was wrong about that. I wonder why the error isn't detected at
expand-time. Does R6RS really require this type of error to be reported
at run-time?
"A reference to an identifier introduced into the output of a
transformer refers to [...] the closest enclosing lexical binding where
the identifier appears (within a syntax <template>) inside the
transformer body or one of the helpers it calls."
That "or one of the helpers it calls" suggests to me that templates in
helpers are also hygienic, regardless of where those helpers are from.
Sorry, that was intended to be a response to Lynn's comments about the
hygiene of templates. That's not really relevant to what I put it under
above. I found that quote while looking for R6RS language relevant to
when template errors should happen (I haven't found any yet). Sleep
helps.
--
: Derick
----------------------------------------------------------------
Lynn Winebarger
2009-04-22 04:43:33 UTC
Permalink
On Tue, Apr 21, 2009 at 3:04 AM, Derick Eddington
Post by Derick Eddington
I think Larceny's phase mismatch detection is very preferable to
allowing separate namespaces (that would be very unSchemely) for
different phases such that `cons' would be allowed unbound at phase -1
but bound at the phases it's imported for.  And this detection is
consistent with the fact that R6RS requires preventing importing the
same name but different bindings at different phases.
At the risk of carrying on an exchange in the wrong forum, I can't
find such a requirement by R6RS (preventing the importing the
same name but different bindings at different phases). What I
have seen indicates the opposite is true - see the response to
formal comment #92 for example.

Lynn
Abdulaziz Ghuloum
2009-04-22 05:10:09 UTC
Permalink
Post by Lynn Winebarger
At the risk of carrying on an exchange in the wrong forum, I can't
find such a requirement by R6RS (preventing the importing the
same name but different bindings at different phases). What I
have seen indicates the opposite is true - see the response to
formal comment #92 for example.
Section 7.1, page 23:

An identifier can be imported with the same local name
from two or more libraries or for two levels from the same
library only if the binding exported by each library is
the same (i.e., the binding is defined in one library, and
it arrives through the imports only by exporting and re-
exporting). Otherwise, no identifier can be imported mul-
tiple times, defined multiple times, or both defined and
imported. No identifiers are visible within a library except
for those explicitly imported into the library or defined
within the library.

Aziz,,,

Abdulaziz Ghuloum
2009-04-21 11:59:10 UTC
Permalink
Post by Lynn Winebarger
(import (rnrs))
;;; Autoloading ...
Post by Lynn Winebarger
(free-identifier=? #'car #car)
Error: syntax-violation: invalid reference: Attempt to use binding
of car in library () at invalid level -1. Binding is only
available at levels: 0 1 car
Entering debugger; type "?" for help.
debug>
(free-identifier=? #'car #'cdr)
#f

No phase error here. Hmmm.

Aziz,,,
Andre van Tonder
2009-04-21 13:19:03 UTC
Permalink
This ticket describes a conciously chosen feature.
Post by Lynn Winebarger
(import (for (rnrs) run expand)
(rename (only (rnrs base) cons) (cons kons)))
(free-identifier=? #'cons #'kons)
Syntax violation: invalid reference
Attempt to use binding of cons in library (program~19XamJ~2) at
invalid level -1. Binding is only available at levels: 0 1 2
This is a concious design choice in the implementation of FREE-IDENTIFIER=?
The fact that this code does not work in Larceny shows that the code is not
R6RS-compliant. That it works in PLT and Ikarus just shows that their
implementors chose not to worrry about imposing R6RS portability for reasons of
their own.

A syntax error is raised if free-identifier=? succeeds
but either argument is outside its declared level. This is sufficient
to ensure that literals such as ... in syntax-case, DEFINE,
BEGIN, etc., in bodies, UNQUOTE, etc. in quasiquote, etc., are used at
the correct level. See the examples below for more discussion, and for
an explanation of why the levels should not be checked if the
comparison fails.

The fact that some implementations do not impose this restriction is irrelevant.
What is relevant is that R6RS allows and in fact encourages implementations
(present or future) to check phases of references, and one of goals of my
expander was to try to ensure maximum portability to other R6RS implementations
by doing as many checks as possible.


The following discusses this further and was taken from
http://www.het.brown.edu/people/andre/macros/december-27-07/examples.scm


;;=================================================================
;;
;; Phase checking for literals and free-identifier=? uses:
;;
;; R6RS does not say if these operations count as references,
;; but does require importing of literals such as ... and _
;; into the appropriate phase. To ensure maximal portability,
;; a good implementation should have a liberal interpretation of
;; what a reference is, and check literal phases.
;; We do so by checking that the arguments of free-identifier=?
;; are both in phase when the result of the comparison is #t
;; (for examples why, see below).
;; For ensuring that uses of literals are in phase, this is sufficient
;; but may be more than necessary. However, we are within our R6RS
;; rights to be more restrictive, and in this case more is
;; better for maximal portability checking.
;;
;;=================================================================

;; This shows why we should only test that the arguments of free-identifier=?
;; are in phase when the comparison succeeds. The expander should complain
;; about the wrong phase of ..., but should not complain about the phase of
;; LIST in the template, even though LIST is also compared to see if it
;; is bound to R6RS ... during expansion.

;;(library (foo)
;; (export)
;; (import (except (rnrs base) ...)
;; (only (rnrs syntax-case) ...)) ;; .. is in wrong phase
;; (define-syntax list-macro
;; (syntax-rules ()
;; ((_ x ...) (list x ...)))))
;;
;; Attempt to use binding ... in library foo at invalid meta level 1.
;; Binding is only available at meta levels: 0

;; More examples:

(library (foo)
(export test)
(import (rnrs base))
(define-syntax test
(syntax-rules (car)
((_ car) #t)
((_ k) #f))))

;; The literal CAR is used in the wrong phase:
;;
;;(library (bar)
;; (export)
;; (import (for (rnrs base) (meta 21))
;; (foo))
;; (test car))
;;
;; Attempt to use binding car in library bar at invalid meta level 0.
;; Binding is only available at meta levels: 21

;; This is not a literal use, so no problem.

(library (bar)
(export)
(import (for (rnrs base) (meta 21))
(foo))
(test cdr))

;; Here the literal is in the wrong phase at the definition site:

(library (foo)
(export test)
(import (except (rnrs base) car)
(for (only (rnrs base) car) (meta 21)))
(define-syntax test
(syntax-rules (car)
((_ car) #t) ; is this a reference?
((_ k) #f))))

;; The problem is detected as soon as we try to use it:

;;(library (bar)
;; (export)
;; (import (rnrs base)
;; (foo))
;; (test car))
;;
;; Attempt to use binding car in library foo at invalid meta level 0.
;; Binding is only available at meta levels: 21

;; We will not detect the problem if we do not use the literal, though.

(library (bar)
(export)
(import (rnrs base)
(foo))
(test cdr)) ;==> expands without problems

;; To summarize, the arguments of free-identifier=? are only
;; required to be in phase when the comparison succeeds.

(library (foo)
(export test1 test2)
(import (except (rnrs) car)
(for (only (rnrs) car) (meta 21)))
(define-syntax test1
(lambda (form)
(free-identifier=? (syntax car) (syntax cdr))))
(define-syntax test2
(lambda (form)
(free-identifier=? (syntax car) (syntax car)))))

(import (foo))
(test1) ;==> #f

;;(test2)
;;
;; Attempt to use binding car in library foo at invalid meta level 0.
;; Binding is only available at meta levels: 21 22

;; Another example, similar in spirit to the first ... example.

;;(library (foo)
;; (export)
;; (import (rnrs base)
;; (for (prefix (only (rnrs base) quasiquote) meta-) (meta 21)))
;; `(meta-quasiquote 1))
;;
;; Attempt to use binding meta-quasiquote in library foo at invalid meta
level 0.
;; Binding is only available at meta levels: 21

;; Out of phase uses of the literals DEFINE, BEGIN, etc., in
;; bodies are detected in the same way.

;;(library (foo)
;; (export)
;; (import (except (rnrs base) define)
;; (for (only (rnrs base) define))) ; imported but for no levels
;; (define x 1)
;; (display x))
;;
;; Attempt to use binding define in library foo at invalid meta level 0.
;; Binding is only available at meta levels:
Abdulaziz Ghuloum
2009-04-21 14:35:04 UTC
Permalink
Post by Andre van Tonder
A syntax error is raised if free-identifier=? succeeds
but either argument is outside its declared level. This is sufficient
to ensure that literals such as ... in syntax-case, DEFINE,
BEGIN, etc., in bodies, UNQUOTE, etc. in quasiquote,
etc., are used at
the correct level. See the examples below for more
discussion, and for
an explanation of why the levels should not be checked if the
comparison fails.
Where are you quoting this from?

Aziz,,,
Andre van Tonder
2009-04-21 14:40:02 UTC
Permalink
It is a slightly modifed version of what is in my README at

http://www.het.brown.edu/people/andre/macros/index.html

Since I modified it, I should probably not have indented it.
Post by Abdulaziz Ghuloum
Post by Andre van Tonder
A syntax error is raised if free-identifier=? succeeds
but either argument is outside its declared level. This is sufficient
to ensure that literals such as ... in syntax-case, DEFINE,
BEGIN, etc., in bodies, UNQUOTE, etc. in quasiquote, etc., are used at
the correct level. See the examples below for more discussion, and for
an explanation of why the levels should not be checked if the
comparison fails.
Where are you quoting this from?
Aziz,,,
Abdulaziz Ghuloum
2009-04-21 14:58:30 UTC
Permalink
Post by Andre van Tonder
The fact that this code does not work in Larceny shows
that the code is not R6RS-compliant.
Really? When did that happen?
Post by Andre van Tonder
That it works in PLT and Ikarus just shows that their
implementors chose not to worrry about imposing R6RS
portability for reasons of their own.
[quote from Andre's README file]
Of course no one needs to worry about your notes
(which have nothing to do with what's in R6RS).
Post by Andre van Tonder
The fact that some implementations do not impose this
restriction is irrelevant.
But you just made this restriction up! Why would any
implementor impose something you made up?
Post by Andre van Tonder
What is relevant is that R6RS allows and in fact
encourages implementations (present or future) to
check phases of references,
The quote I have from R6RS says:

When an identifier appears as an expression in a
phase that is inconsistent with the identifier’s
level, then an implementation may raise an exception
either at expand time or run time, or it may allow
the reference.

Note: it may, or may. There are no musts or shoulds
or any of that. Where did you get the "encourages"
part from?

Aziz,,,
Andre van Tonder
2009-04-21 17:54:30 UTC
Permalink
[sarcasm deleted]
Lynn Winebarger
2009-04-22 04:55:31 UTC
Permalink
Post by Andre van Tonder
This is a concious design choice in the implementation of FREE-IDENTIFIER=?
The fact that this code does not work in Larceny shows that the code is not
R6RS-compliant.  That it works in PLT and Ikarus just shows that their
implementors chose not to worrry about imposing R6RS portability for reasons
of their own.
Andre,
It seems to me that you are describing a restriction on valid inputs to your
implementation of free-identifier=?, rather than a pure instance of ill-formed
syntax. Please consider changing the exception to have condition type
&assertion as per Section 5.4 instead of &syntax.

The R6RS is obviously ambiguous on this point by allowing identifier reference
errors to be signaled whenever or not at all, but this does not appear to be
a "real" instance of an identifier reference. If nothing else,
changing the condition
type would clarify to the programmer getting the error that the exception is
occuring at run-time (for the library) and that the identifier is being
considered as a bare object rather than as part of a source expression.

Lynn
William D Clinger
2009-04-21 17:01:31 UTC
Permalink
It appears to me that Andre van Tonder and Aziz Ghuloum
agree implementations are allowed to raise an exception
for that test, as Larceny v0.97b1 has been doing since
August 2008. I reported this to the maintainers of PLT
Scheme, who promptly dropped the test in question [1].

Larceny v0.97b1 now passes all but one of the PLT tests.
That means we need to write more test programs.

Will

[1] http://bugs.plt-scheme.org/query/?cmd=view&pr=10210
Abdulaziz Ghuloum
2009-04-21 17:23:23 UTC
Permalink
Post by William D Clinger
It appears to me that Andre van Tonder and Aziz Ghuloum
agree implementations are allowed to raise an exception
for that test
I actually don't know. It's not clear to me from the
description of free-identifier=? in the report that it
is allowed to raise any exception given that its two
arguments are indeed identifiers. Can you point it out?

Andre writes (below the paragraph he quoted before) that:

Note:
This choice raises a syntax violation when MzScheme
and Chez would disagree, thus ensuring that a
program blessed by this expander will be maximally
portable.

Now just because Chez and MzScheme disagree on something
(as they often do) does not by itself justify for a third
implementation to raise an exception (despite the good
intentions). Otherwise, it would be okay for, say, Ikarus
to raise an exception when asked (fixnum? 536870912) on
the ground that any program that's blessed by it will be
maximally portable. Right?

Aziz,,,
Lynn Winebarger
2009-04-21 20:18:59 UTC
Permalink
Post by William D Clinger
It appears to me that Andre van Tonder and Aziz Ghuloum
agree implementations are allowed to raise an exception
for that test
I actually don't know.  It's not clear to me from the
description of free-identifier=? in the report that it
is allowed to raise any exception given that its two
arguments are indeed identifiers.  Can you point it out?
What I find confusing is that a syntax violation is being generated at
phase 0 of a library. I had just assumed that the syntax violation
occurred when the #'cons expression was expanded.

While I think this is an obvious expectation (that syntax violations
occur at expand time), I think R6RS might actually support this
expectation in section 5.5:
If a top-level or library form in a program is not
syntactically correct,
then the implementation must raise an exception with condition type
&syntax, and execution of that top-level program or library must not be
allowed to begin.

So, I agree with Andre's assertion that an implementation free to
raise a syntax exception for this identifier, but I don't think it's
free to cause the syntax exception as an invalid argument to
free-identifier=? at run-time. Maybe there is some other kind of
exception that could be raised at run-time for an invalid argument,
just not one with an &syntax condition type.

Lynn
William D Clinger
2009-04-22 00:57:41 UTC
Permalink
Post by Abdulaziz Ghuloum
Post by William D Clinger
It appears to me that Andre van Tonder and Aziz Ghuloum
agree implementations are allowed to raise an exception
for that test
I actually don't know.
I apologize for mischaracterizing your position. For
reasons explained below, I still think it was best to
remove the controversial test.
Post by Abdulaziz Ghuloum
It's not clear to me from the
description of free-identifier=? in the report that it
is allowed to raise any exception given that its two
arguments are indeed identifiers. Can you point it out?
Although the R6RS documents are quite vague concerning
the semantics of identifiers and phase levels, and the
vagueness was partly deliberate, they do contain a few
statements that can be interpreted in favor of one
argument or the other.

The following paragraph of R6RS 7.2 appears to suggest
that one purpose of negative phases is to require the
phase of an identifier to be used only at some shifted
phase, and the word "must" suggests that implementations
that support negative phases for this purpose are
allowed, or arguably required, to raise exceptions if
there is a phase mismatch:

Macro expansion within a library can introduce a
reference to an identifier that is not explicitly
imported into the library. In that case, the phase
of the reference must match the identifier's level
as shifted by the difference between the phase of
the source library (i.e., the library that supplied
the identifier's lexical context) and the library
that encloses the reference. For example, suppose
that expanding a library invokes a macro transformer,
and the evaluation of the macro transformer refers to
an identifier that is exported from another library
(so the phase-1 instance of the library is used);
suppose further that the value of the binding is a
syntax object representing an identifier with only a
level-n binding; then, the identifier must be used
only at phase n + 1 in the library being expanded.
This combination of levels and phases is why
negative levels on identifiers can be useful, even
though libraries exist only at non-negative phases.

Two paragraphs later, R6RS 7.2 explicitly says that
implementations are allowed to raise exceptions when
there is a phase mismatch:

When an identifier appears as an expression in a
phase that is inconsistent with the identifier's
level, then an implementation may raise an exception
either at expand time or run time, or it may allow
the reference.

That reduces the question to one of whether the R6RS
was actually meant to allow the particular phase levels
implemented by Andre van Tonder's macro expander.

In the full text of Andre's message that was truncated
by a Mailman bug, he quoted R6RS 7.2 [1]:

A level is a lexical property of an identifier that
determines in which phases it can be referenced.
Post by Abdulaziz Ghuloum
In the absence of a definition of what it means to
"reference" an identifier (despite a formal comment by
me on this precise point, asking for clarification,
that was ignored), and whether literal uses count as
identifiers, the interpretation assumed by the
expander in Larceny is just as valid as any other.
I am not certain of the formal comment to which Andre
is referring, but the R6RS editors' response to Andre's
formal comment 92 states [2]:

....A precise specification of the library system
remains elusive, partly because different
implementors still have different ideas about how
the library system should work....

The different opinions are supported by two
different reference implementations of R6RS
libraries: one by Van Tonder and one by Ghuloum and
Dybvig. In addition, PLT Scheme implements a
library system...

Despite the differences in the reference
implementations, it appears that many programs will
run the same in both variants of the library system.
The overlap appears to be large enough to support
practical portability between the variants.

Under the assumption that the overlap is useful, and
given the lack of consensus and relative lack of
experience with the two prominent variants of draft
R6RS libraries, the R6RS specification of libraries
should be designed to admit both of the reference
implementations. As a design process, this
implementation-driven approach leaves something to
be desired, but it seems to be the surest way forward.

I would not argue that the R6RS editors had this particular
issue in mind when they made those remarks, but it is clear
that (1) they wished to allow different implementations
that reflect different opinions, (2) they regarded the
overlap between van Tonder's system and others as "large
enough to support practical portability", and (3) they
felt that the R6RS specification should admit van Tonder's
reference implementation.

In response to Andre's formal comment 135, which requested
a less operational description that would clarify the
distinction between accidental properties of the expansion
algorithm described by the R6RS document and the properties
that were meant to be required of actual implementations,
the editors noted that the operational description "is not
incompatible" with whatever semantics they were attempting
to express [3].

In my opinion, the editors' response to formal comment 135
signalled their intention to stay out of the business of
specifying corner cases that have little relevance to
practical portability.

Note also that the editors' response to formal comment 135
effectively declined to consider Andre's implementation
non-conformant, which was an alternative that Andre had
explicitly offered as part of his formal comment.

The specifications of the syntax special form and of the
free-identifier=? procedure that appear within R6RS library
section 12.4 and 12.6 are amazingly unhelpful, partly due
to the operational flavor of the specifications that Andre
had complained about in formal comment 135. Neither says
anything about phase levels, even though the syntax special
form is said to be analogous to the quote special form,
which is generally believed to have the following genuinely
radical interaction with phase levels: it ignores them and
converts identifiers into symbols.

Of course, the R6RS specification of quote says no such
thing. An R6RS-conforming implementation could presumably
arrange for quoted identifiers to evaluate to identifiers,
not to symbols. The apparent fact that legalistic reading
of the R6RS would allow such implementations illustrates
the folly of demanding that implementors justify every
corner case by citing chapter and verse of the R6RS. At
some point, we just have to rely on our interpretations
of what the editors were attempting to say.

With respect to the semantics of macro expansion and
associated features such as syntax and free-identifier=?,
it is a matter of record that the R6RS editors knew their
semantics was ambiguous, and that one of the reasons for
the ambiguities was that they wished to allow Andre van
Tonder's reference implementation.

The non-binding, largely unvetted, and never-ratified
R6RS rationale section 7.5 supports that interpretation:

Opinions vary on how libraries should be instantiated
and initialized during the expansion and execution of
library bodies, whether library instances should be
distinguished across phases, and whether levels should
be declared so that they constrain identifier uses to
particular phases. This report therefore leaves
considerable latitude to implementations, while
attempting to provide enough guarantees to make
portable libraries feasible.

Characteristically, the very next paragraph undermines
that statement by making what appears to be a factually
incorrect claim:

Note that, if each right-hand side of the keyword
definition and keyword binding forms appearing in a
program is a syntax-rules or identifier-syntax form,
syntax-rules and identifier-syntax forms do not appear
in any other contexts, and no import form employs for
to override the default import phases, then the program
does not depend on whether instances are distinguished
across phases, and the phase of an identifier's use
cannot be inconsistent with the identifier's level.

If that were true, then a program that uses syntax-case
only as a top-level expression, outside of any binding
form, and satisfies the other conditions, could not have
phasing errors. That's nuts. I think it's pretty clear
that the R6RS editors mostly thought about syntax forms
as things that would never be used except in connection
with a syntax-case form. I also suspect that's part of
why the R6RS doesn't give a coherent semantics for the
syntax special form when used at level 0.
Post by Abdulaziz Ghuloum
Now just because Chez and MzScheme disagree on something
(as they often do) does not by itself justify for a third
implementation to raise an exception (despite the good
intentions). Otherwise, it would be okay for, say, Ikarus
to raise an exception when asked (fixnum? 536870912) on
the ground that any program that's blessed by it will be
maximally portable. Right?
Wrong. The specification of fixnum? unambiguously states
that it returns #t or #f. Furthermore the R6RS definition
of fixnums explicitly refers to an implementation-dependent
range, so the implementation-dependence was deliberate and
anticipated.

The R6RS clearly says that implementations may raise
exceptions when identifiers are used inconsistently with
their level. That phase levels are at least partially
implementation-dependent is alluded to within the R6RS
documents, and is even more clearly attested by the
historical record. The R6RS editors have stated several
times that this lack of clarity was partially deliberate,
and was motivated in part by a desire to allow a variety
of implementations. Furthermore, they explicitly named
van Tonder's reference implementation as one they wished
to allow.

Andre and I may be wrong about what the R6RS documents say
about this, but I have now wasted enough time on this to
believe that the burden of proof lies with those who wish
to maintain that the R6RS forbids Larceny's behavior on
that test case. Even if that were so, it is such a bizarre
corner case that I do not foresee any practical consequences
that would be likely to result from leaving such a thing
unfixed, and would probably be inclined to leave it unfixed
in Larceny until such time as the R6RS is superseded by some
other standard.

Will


[1] http://lists.ccs.neu.edu/pipermail/larceny-users/2009-April/000649.html
[2] http://www.r6rs.org/formal-comments/comment-92.txt
[3] http://www.r6rs.org/formal-comments/comment-135.txt
Continue reading on narkive:
Loading...