[R6RS] safe and unsafe; declarations
dyb at cs.indiana.edu
dyb at cs.indiana.edu
Sat Mar 4 09:27:26 EST 2006
To bring the scoping rules for declarations in line with lexical scoping
and referential transparency, and also to limit unnecessary freedom to
ignore safe declarations in some cases where unsafe and safe declarations
are mixed, I would like propose the following treatement of declarations.
* A declaration appearing in a <body> associates the specified safe,
fast, small, and debug priorities with each free identifier within the
<body>, except where the declaration is shadowed by a conflicting
* If the declare keyword of a declaration is introduced into the output
of a macro, the declaration affects only the identifiers introduced by
the macro. Similarly, an identifier introduced by the macro is
affected only by such declarations and declarations whose scope
includes the macro definition.
* A standard syntactic form (recognized as such because its identifying
keyword is an identifier reference resolving to a keyword imported from
the r6rs core language or standard library) whose identifying keyword
has safe priority 0 has unspecified behavior if a situation for which
the implementation is allowed or required to raise an exception arises
directly from the use of the syntactic form.
* A standard procedure reference (recognized as such because it is an
identifier reference resolving to a variable binding imported from the
r6rs core language or standard library) may evaluate to different
versions of the standard procedure depending on the priorities
associated with the reference. In particular, if the standard
procedure reference is associated with safe priority 0, the standard
procedure reference may evaluate to an unsafe version of the standard
procedure. An unsafe version has unspecified behavior if a situation
for which the implementation is allowed or required to raise an
exception arises directly from the use of that standard procedure.
* Assuming unbound variables are possible in the standard, the behavior
of an unbound reference to a variable associated with safe priority 0
This proposal differs from Will's proposal, as I understand it, in the
1. The scope of a declaration is more precisely specified (and possibly
different from what Will had in mind).
2. It requires that safe code embedded within or run from unsafe code
must raise all required exceptions, as long as no potentially
exception-causing situation arises directly from the use of the unsafe
Also, the notion that exceptions are always raised but are handled in an
unspecified manner in unsafe mode, which Mike, at least, found confusing,
is gone, but I believe that's an expository detail only.
The following illustrates difference 2. Let's assume that all of the free
identifiers in the code below resolve to the corresponding standard
keyword or procedure bindings. Let's also assume that the standard
procedure to which + is bound is required to raise a &nonnumeric exception
if any of its arguments are not numbers and that it returns normally
without raising an exception in all other cases. Then the expression:
(let ([x (+ 3)])
has unspecified behavior with Will's proposal, even though the outer call
to (possibly unsafe) + returns normally, because the inner safe
declaration can be ignored. It must raise a &nonnumeric exception with
(+ 'a (begin (declare safe) (+ 'b))))
has unspecified behavior with Will's proposal but must raise a &nonnumeric
exception with mine. The first argument to the outer (possibly unsafe) +
is not a number, but the exception is raised by the inner call to + before
the outer call is made, due to applicative-order evaluation.
In both systems,
(let ([x (+ 'a)])
has unspecified behavior, because the first (possibly unsafe) + receives a
non-numeric argument, at which point all bets are off.
Note that an implementation is free to ignore all declarations with either
proposal, but with mine it is effectively required not to ignore a safe
declaration that appears within the scope of an unsafe declaration if it
does not also ignore the unsafe declaration.
A likely side implication of my proposal is that eqv? might return either
#t or #f when applied to two versions of a primitive that might differ due
to differing declarations. For example,
(eqv? (begin (declare safe) +)
(begin (declare unsafe) +))
can return either #t or #f. This doesn't bother me in the slightest,
but we could require that eqv? return #t in situations such as this.
In practice, this would likely cause most implementations to use the
same (default) version of the standard procedure whenever the value
If we agree to this proposal, then I also intend to suggest that an
identifier-priority procedure be included in the standard syntax-case
system. This procedure can be used to extract a priority associated with
an identifier, e.g.:
(syntax-case x ()
[(k (x ...) e)
(if (= (identifier-priority 'safe #'k) 0)
This will allow user-defined syntactic forms to be sensitive to declared
priorities. In conjunction with identifier macros, it will also allow a
similar effect for user-defined procedures.
More information about the R6RS