问题
I am struggling with what is the precise semantics of throw/1 without a suitable catch/3 in ISO Prolog. I am reading the ISO Prolog specification, and it seems to me that the execution will end up in an infinite recursion.
(Note that you must have access to the ISO Prolog standard in order to interpret this question).
Step 1. Let's say we call throw(unknown), and there is no catch/3 on the stack whatsoever.
Step 2. It will end up in 7.8.10.1 c), saying that it will be an system error (7.12.2 j).
Step 3. This is the same kind of formulation used at other places for other places, so I assume it should be interpreted in the same way. Therefore 7.12.1 applies, and the current goal (throw/1) will be replaced by throw(error(system_error, Imp_def)).
Step 4. Executing this goal will find no active "catch" for it on the stack. It should therefore attempt the same steps, continue at Step 2, and so forth => an infinite recursion.
You may say that the transformation of an uncaught "throw" to a system_error is "final" and not subject to further handling as other errors are, and it probably has to be so, in order to avoid the problem I described, but my question is, where in the standard is this covered?
Some other notes, for completeness:
Note 4 in 7.12.2 also mentions a possibility of a System Error under these circumstances. I think that the formulation used there ("....there is no active goal catch/3") introduces some confusion in yet another respect, because it should qualify that with the condition that the Catcher must unify with the error term (B).
What is the idea behind transforming uncaught throw-s to a system error? It looks like that it may exist to make a life "easier" for a top-level of the Prolog processor, so that it only receives one, predictable, kind of errors? To me, that brings more problems than benefits - the true cause of the error would thus disappear - any opinion or comment?
The formal semantics (Annex A) seems to struggle with this somehow as well, although I have not studied it to the detail. In A.2.5, it mentions that "... However in the formal specification there is a catcher at the root...", and relates it, for example, to the execution of findall/3. So the formal spec differs here from the body text?
回答1:
(We are talking here about ISO/IEC 13211-1:1995)
The definition of the control construct throw/1
(7.8.10) states in two places, that in this situation there shall be a system error. First, as you have observed, there is 7.8.10.1 c:
c) It shall be a system error (7.12.2 j) if S is now
empty,
and then, there is the error clause:
7.8.10.3 Errors
a)
B
is a variable
—instantiation_error
.b)
B
does not unify with theC
argument of any call
of catch/3
—system_error
.
To see what a system error is, we need to look at subclause 7.12.2 j:
j) There may be a System Error at any stage of
execution. The conditions in which there shall be a
system error, and the action taken by a processor after
a system error are implementation dependent. It has the
formsystem_error
.
So, the action taken by a processor after a system error is implementation dependent. An infinite loop would be fine, too.
In a standard conforming program you cannot rely on any particular action in that situation.
Ad Note 1: A Note is not normative. See 1.1 Notes. It is essentially a summary.
Ad Note 2: This is to avoid any overspecification. Parts as these are kept as vague as possible in the standard because a system error might have corrupted a Prolog system. It is quite similar with resource errors. Ideally a system would catch them and continue execution, but many implementations have difficulties to guarantee this in the general case.
Ad Note 3: The formal semantics is quintessentially another implementation. And in some parts, an implementation has to make certain decisions, whereas a specification can leave open all possibilities. Apart from that, note that the formal semantics is not normative. It did help to debug the standard, though.
Edit: In the comments you say:
(1mo) So you are saying that this allows the processor to immediately do its implementation dependent action when a system error occurs - not only after it is uncaught? (2do) Thus relieving it from the necessity to apply the goal transformation in 7.12.1 ? (3tio) It would then also be OK if system errors are not catchable (by catch/3) at all?
1mo
The sentence in 7.12.2 j
... the action taken by a processor after a system error are implementation dependent.
effectively overrules 7.12.1. Similarly to 7.12.2 h which may occur "at any stage of execution", too.
Just to be sure that we are reading the codex correctly assume the opposite for a moment. Imagine that a system error occurs and 7.12.1 would now produce such an error that is not caught anywhere, then we again would have a system error etc. Thus: the sentence above would never apply. This alone suggests that we have read here something incorrectly.
On the other hand, imagine a situation where a system error occurs when the system is completely corrupted. How should now 7.12.1 be executed anyway? So the Prolog system would be unable to execute this loop. Does this mean that a Prolog processor can only be conforming if we can prove that there will never be a system error? This is practically impossible, in particular, because
7.12.2 Error classification
NOTES
...
4 A System Error may happen for example (a) in interactions
with the operating system (for example, a disc crash or interrupt),
...
So effectively this would mean that there cannot be any conforming Prolog processor.
2do
7.12.1 describes a way how an error is handled within Prolog. That is, if you are able to handle the error within Prolog, then a Prolog system should use this method. However, there might be situations where it is very difficult or even impossible (see above case) to handle an error in Prolog at all. In such situations a system might bail out.
3tio
Short answer: Yes. It is a quite extreme, but valid, reading that system errors are not catchable and execution would be terminated then. But maybe, take first a step back to understand (a) what a technical standard is for and what not, (b) the scope of a standard.
Scope
Or, rather start with b: In 1 Scope, we have:
NOTE - This part of ISO/IEC 13211 does not specify:
...
f) the user environment (top level loop, debugger, library
system, editor, compiler etc.) of a Prolog processor.
(Strictly speaking this is only a note, but if you leaf through the standard, you will realize that these aspects are not specified.) I somewhat suspect that what you actually want is to understand what a toplevel loop shall do with an uncaught error. However, this question is out of scope of 13211-1. It probably makes a lot of sense to report such an error and continue execution.
Purpose
The other point here is what a technical standard is actually for. Technical standards are frequently misunderstood to be a complete guarantee that a system will work "properly". However, if a system is conforming to a technical standard, this does not imply that it is fit for any purpose or use. To show you the very extreme, consider the shell command exit 1
which might be considered to be a processor conforming to 13211-1 (provided it comes accompanied with documentation that defines all implementation defined features). Why? Well, when the system starts up, it might realize that the minimal requirements (1 Scope, Note b) are not met and thus it produces a system error which is handled by producing error code 1.
What you actually want is rather a system that does conform and is fit for certain purposes (that are out of scope of 13211-1). So in addition of asking yourself whether or not a certain behavior is conforming or not, you will also look about how this system is fit for certain purposes.
A good example to this end are resource errors. Many Prolog systems are able to handle certain resource errors. Consider:
p(-X) :- p(X).
and the query catch(p(X),error(E,_),true).
A system has now several opportunities: loop infinitely (which requires a very, very smart GC), succeed with E = resource_error(Resource)
, succeed with E = system_error
, stop execution with some error code, eat up all resources.
While there is no statement that a system has to catch such an error, all the mechanism is provided in the standard to do so.
Similarly in the case of a system_error
: If it makes sense, it might be a good idea to report system errors properly within Prolog, however, if things go too far, bailing out safely is still not the worst that can happen. In fact, the worst (yet still conforming) way would be to continue execution "as if" everything is fine, when it is not.
来源:https://stackoverflow.com/questions/25124946/throw-without-catch-in-iso-prolog