Formal comment #176 (defect)
Raising of exceptions should not be ambiguous or confusing
Reported by: William D Clinger
Version: 5.92
In an effort to obtain a clearer picture of what the editors of the
5.92 draft might perhaps have intended to require of implementations,
I searched for every occurrence of the word "raise" or "raised". Some
of what I found is ambiguous, confusing, or contradictory. Herewith
details.
This comment is minor by comparison with the preposterous mustard of
the 5.92 draft, but it would make sense to try to clean all of this up
in the next draft.
****************************************************************
The first paragraph of section 4.3 (Exceptional situations) begins:
A variety of exceptional situations are distinguished in this
report, among them violations of syntax, violations of a
procedure's specification, violations of implementation
restrictions, and exceptional situations in the environment. When
an exception is raised, an object is provided that describes the
nature of the exceptional situation.
There is no necessary connection between those two sentences. In
particular, those sentences do not say whether an exception
must/should/may be raised in response to an exceptional situation. I
assume that was deliberate.
In the third paragraph of section 4.3, "implementations are required
to raise an exception" is probably intended to mean that
"implementations must raise an exception" under the circumstance
described in that paragraph.
The fifth paragraph of section 4.3 says "implementations are required
to report violations of implementation restrictions", but then says
they "may" raise an &implementation-restriction exception when one
particular kind of implementation restriction is violated. Section 4.5
(Safety) then says "must" again. This is confusing and contradictory:
Does the draft mean "may" or "must"?
****************************************************************
The first two paragraphs of section 4.4 (Argument checking) say that
implementations are "responsible for checking that the restrictions in
the specfication are indeed met, to the extent that it is reasonable,
possible, and necessary to allow the specified operation to complete
successfully" and are "encouraged to perform as much checking as
possible and give detailed feedback about violations".
That gives implementations a lot of wiggle room; sounds like a
"should". The last paragraph of section 4.4 then says:
When an implementation detects a violation of an argument
specification at run time, it must either raise an exception with
condition type &violation, or abort the program in a way
consistent with the safety of execution as described in the next
section.
In context, following the first two paragraphs of section 4.4, I
interpret the above to mean (1) implementations are not required to
detect such violations at all and (2) even if they detect such a
violation, they may abort the program without raising an exception.
That interpretation, alas, applies only to situations for which the
report gives no specific guidance to the contrary.
****************************************************************
The second paragraph of section 4.7 (Multiple return values) says:
....If the number of return values passed to a continuation
created by a call to call-with-values is not accepted by its
consumer that was passed in that call, then an exception is
raised....
According to section 5.5 (Exceptional situations), the above means an
exception must be raised. If the consumer is written in some language
other than Scheme, however, then the situation may not be
detectable. Are we to conclude that conforming implementations cannot
allow consumers to be written in a language/implementation that does
not expose its procedures' arity at run time?
The specification of call-with-values in section 9.17 has the same
problem.
****************************************************************
The sixth paragraph (or thereabouts) of section 5.2 (Entry
format) says:
Descriptions of syntax may express other restrictions on
the components of a form. Typically, such a restriction is
formulated as a phrase of the form "x must be a ...".
As with implicit restrictions, such a phrase means that
an exception with condition type &syntax is raised if the
component does not meet the restriction.
According to section 5.5 (Exceptional situations), this means
an exception "must" be raised, but whether such syntactic
restrictions are met is usually undecidable. (It is already
undecidable whether the macro expander will ever finish its
expansion of the syntax.)
Later in section 4.4, near the top of the left column of page
21, we read:
A procedure that is called with an argument that it is not
specified to handle raises an exception with condition type
&assertion. Also, if the number of arguments provided in
a procedure call does not match any argument count specified
for the called procedure, an exception with condition type
&assertion is raised.
The first sentence does not match the magic pattern defined
by section 5.5; is it must/should/may? The discussion in
section 4.4, together with the fact that the situations
covered by that sentence are often undecidable, argue that
the sentence is trying to say "should". It should just say
"should" and be done with it.
According to section 5.5, the second sentence means "must",
but the situation described in the second sentence cannot
always be detected when calling procedures written in languages
other than Scheme. Again, are we to conclude that conforming
implementations cannot permit calls to procedures written in
other languages?
****************************************************************
Section 5.3 (Evaluation examples) does not match the magic
language whose meaning is defined by section 5.5, and thus
does not say whether examples such as
(integer->char #xD800) ==> &assertion exception
mean the example must/should/may raise an exception, leaving
that to be explained for each particular example. I assume
this was deliberate.
****************************************************************
The first four paragraphs of section 5.5 (Exceptional
situations) are clear enough, but the last paragraph says:
For example, an exception with condition type &assertion
is raised if a procedure is passed an argument that the
procedure is not explicitly specified to handle, even
though such domain exceptions are not always mentioned
in this report.
That means "must", but that's hard to square with section 4.4,
which correctly notes that many of those domain exceptions are
undecidable or impractical for implementations to detect.
****************************************************************
Section 9.5.6 (Binding constructs) says an exception must be
raised if the letrec or letrec* restriction is violated. Even
apart from the fact that detecting those restrictions is an
undecidable problem (as those restrictions are defined by 5.92),
I think it is a terrible idea to require implementations to
detect and to raise an exception in response to violations of
those restrictions. Doing so legitimizes situations that, in
R5RS Scheme, are clearly an error. Consider, for example:
(library (foolish)
(export raise-an-exception)
(define (raise-an-exception . args)
(letrec ((x x)) x)))
According to the 5.92 draft, the library above is a perfectly
legal way to define a procedure that raises an &assertion
exception whenever it is called.
The absurdity of allowing code such as the above is exceeded
only by the absurdity of its rationale: Some editors were
unhappy that some R5RS-conformant implementations failed to
detect violations of the letrec restriction, so they invented
a semantics for the R5RS error situation and are proposing to
require all implementations (1) to allow the error situation
and (2) to give it the newly invented semantics.
One could make similar arguments against many of the other
R5RS error situations that are well on their way to being
allowed by R6RS.
****************************************************************
Section 9.9 contains language that appears to be incompatible
with the usage defined in sections 5.1 and 5.5. Consider, for
example, this sentence from the specification of expt:
For other cases in which the first argument is zero, an
exception is raised with condition type
&implementation-restriction or an unspecified number is
returned.
In other words, it is an absolute requirement for those cases
to raise an exception, but they don't have to.
****************************************************************
In section 9.14, two examples for string-set! say attempts to
mutate an immutable string "should" raise an exception, but the
corresponding example for vector-set! says "may". The
vector-set! example should say "should", not "may".
****************************************************************
I will deal with section 10 (Formal semantics) in a separate
formal comment. With respect to the raising of exceptions,
I think most of the formal semantics' errors correspond to
errors in the informal specifications.
****************************************************************
Library section 5.2 (Explicit-naming syntactic layer) contains
another example of language that appears to be incompatible
with the usage defined in sections 5.1 and 5.5:
If this condition is not met, it is either considered a
syntax violation or an exception with condition type
&assertion is raised.
****************************************************************
The first paragraph of library section 7.1 (Condition types)
begins with
In exceptional situations arising from "I/O errors", the
procedures described in this chapter raise an exception
with condition type &i/o.
I believe that should be "should raise". Whether a situation
arises from "I/O errors" is undecidable. (I may have told
some of you about the two-hour page fault my PowerBook 170
completed after the power finally came back on and the external
SCSI drive spun back up. Where I now live, whether the power
would have come back on is undecidable.)
****************************************************************
In library section 7.2.4, the specifications of eol-style and
error-handling-mode are explicitly implementation-dependent,
but then say:
Otherwise, an exception is raised.
I think that should be "should be raised"; otherwise we have
an absolute requirement that implementations are allowed to
define in their own nefarious ways.
****************************************************************
In library section 12 (Enumerations), the specification of
enum-set-constructor ends with:
If any value in the list is not a symbol that belongs to
the universe, then the unary procedure raises an exception
with condition type &assertion.
Must/should/may? I presume "must", but this should be explicit
since it isn't covered by the magic phrasing defined in section
5.5 of the main document. (I probably missed several similar
uses of "raises".)
****************************************************************
In library section 15 (eval), the specification of eval says:
If the first argument to eval is not a syntactically
correct expression, then eval must raise an exception
with condition type &syntax.
Thanks to macro expansion, whether the first argument to
eval is a syntactically correct expression is undecidable.
The specification should say "should", not "must", else the
language will be unimplementable. Alternatively, the
specification could be changed to say something like
If, during or after its macro expansion, the first argument
to eval is determined not to be a syntactically correct
expression, then eval must raise an exception with condition
type &syntax.
I think the sentence that follows the one I quoted is okay.
The second paragraph of the specification of environment says
If eval is applied to an expression that attempts to assign
to one of the variables of the environment, eval must raise
an exception with a condition type &assertion.
That sounds too weak to me. I think the intent of the editors
was more like this:
If eval is applied to an expression that contains an
assignment to one of the variables of the environment,
then eval must raise an exception with a condition type
&assertion.
Maybe I'm wrong. That too is undecidable in general, although
specific instances may be easily decided.
****************************************************************
Will
RESPONSE:
The editors will attempt to clarify most of the issues raised in the
formal comment.
Exceptions:
- "Multiple return values": Consumers with unspecified arity would
have to come from some library, which could be declared unsafe to
ensure the underlying implementation is conformant with the report.
- "Entry format": The same argument applies to procedures written in
other languages.