ISO/IEC JTC1 SC22 WG17 N226
An error class for unexpected instantiations

Ulrich Neumerkel and Markus Triska, 2010-07-26 (Version history)

Contributors

Jan Wielemaker (The Netherlands). Tom Schrijvers (Belgium). Alexandre Miguel Pinto (Portugal). John Gallagher (Denmark). Ulrich Geske (Germany). Neng-Fa Zhou (United States). Christoph Beierle (Germany). Dietmar Seipel (Germany). Mike Elston (New Zealand). Mats Carlson (Sweden). Feliks Kluzniak (United States). Samer Abdallah (United Kingdom). Michael Hanus (Germany). Joachim Schimpf (Australia). Daniel Diaz (France). Vítor Santos Costa (Portugal). Klaus Dler (Germany). Manuel Hermenegildo (Spain). Jonathan Hodgson (United States). Katsuhiko Nakamura (Japan). Roger Scowen (United Kingdom). David S. Warren (United States).

History

2008-02-24: First e-mail report to WG17.
2009-07-17: Action item in Pasadena, 2009.
2009-10-29: Delivered to WG17 as N213.
2010-07-21: Resolution in Edinburgh: uninstantiation_error.
2010-07-26: N226.
2010-12-13: Superseded by DTC2.
2012-02-14: Published within ISO/IEC 13211-1:1995/Cor.2:2012.
Changes under version control.

Motivation

In ISO/IEC 13211-1:1995 subclause 8.11.5.3 f, a goal open(File, Mode, Stream) has the following error defined:
f) Stream is not a variable
type_error(variable, Stream)
The argument Stream should be unified with a stream-term (7.10.2), more specifically (7.10.2.1):
A standard-conforming program shall make no assumptions
about the form of the stream-term, except that:
a) It is a ground term.
b) It is not an atom.
c) It uniquely identifies a particular stream during the
time that the stream is open.
It is implementation dependent whether or not the pro-
cessor uses the same stream-term to represent different
source/sinks at different times.
It is therefore possible for a standard conforming processor to reuse stream-terms for different mutually exclusive file operations. This can be observed in many processors such as SICStus 3, SWI, YAP.
YAP version Yap-6.0.0
   ?- open(t, write, S), close(S), open(t, write, S).
     ERROR!!
     TYPE ERROR- open(t,write,$stream(3)): expected unbound variable, got $stream(3)
   ?- open(t, write, S), close(S), open(t, write, S2), S == S2.
S = S2 = '$stream'(3) ? ;
no
Whereas the first query produces the required type_error(variable,S), the second clearly shows that success could have been possible.

In all other situations, type errors mean semantically failure. Type errors are signaled in situations "where the type of an argument or one of its components is incorrect but not a variable" (7.12.2 b). This particular case is the only exception. In fact, the intention of type errors was to replace silent failures previously found in most systems by more meaningful messages. So only in situations where a silent failure (or another erroneous outcome) would happen otherwise, type errors appear appropriate. Clearly, this is not the case here.

An implementation that did guarantee unique identifiers was Quintus Prolog according to Richard O'Keefe. Uniqueness was guaranteed as long as identifiers were referenced within the system. Nevertheless, even such an implementation profits from an error for an instantiated stream as it would prevent opening a file that can no longer be closed explicitly.

The error classification of 13211-1 (7.12.2) has turned out to be surprisingly robust, even for unanticipated cases. It seems that the particular situation is the only one where an existing error needs a new error class.

Why now?

Currently, the proposed change affects only open/4,3 (8.11.5) in an existing standard. However, this kind of error is also needed in the following areas: Brief, this is the last moment to adjust to avoid otherwise needless overheads ahead!

The following technical corrigendum reflects the resolution in Edinburgh 2010.

Draft to part of the second technical corrigendum for 13211-1:1995

7.12.2 Error classification

Clause b: Remove variable from the enumerated set.

Add additional clause k:

k) There shall be an Uninstantiation Error when an argument or one of its components is not a variable, and a variable or a component as variable is required. It has the form uninstantiation_error(Culprit) where Culprit is the argument or one of its components which caused the error.

8.1.3 (The format of built-in predicate definitions) Errors

Replace in Note 5
a type error
by
an uninstantiation error

8.11.5.3 (open/4, open/3) Errors, clause f

Replace
type_error(variable, Stream).
by
uninstantiation_error(Stream).

Below is the actual discussion that led to the name uninstantiation error. We will thus first look at the existing conventions in 13211-1.

An error for unexpected instantiations can easily be circumvented in case no error should be produced. If a goal p(Term) leads to this error, the error can be prevented by using p(NewVar), NewVar = Term.

Error classes in 13211-1 7.12.2

Part 1 defines the following error classification. For each error term we give an explanation of the error situation and an example where that error can be observed. The new error class has been added as clause k.
Error_termError situationExample
a)

instantiation_error

An instantiation is expected, but a variable is found instead. X =.. Y.
instantiation_error.
b)

type_error(ValidType, Culprit)

A term of type ValidType is expected, but Culprit is found instead. X =.. [foo|bar].
type_error(list, [foo|bar]).
c)

domain_error(ValidDomain, Culprit)

Similarly, a domain is expected. X =.. [].
domain_error(non_empty_list, []).
d)

existence_error(ObjectType, Culprit)

The existence of Culprit as an object of ObjectType is expected, but it does not exist.

ObjectType ∈ { procedure, source_sink, stream }

ex_nihilo.
existence_error(procedure, ex_nihilo/0).
e)

permission_error(Operation, PermissionType, Culprit)

The permission for doing Operation onto Culprit as of type PermissionType is expected, but the permission is not given.

Operation ∈ { access, create, input, modify, open, output, reposition },

PermissionType ∈ { binary_stream, flag, operator, past_end_of_stream, private_procedure, static_procedure, source_sink, stream, text_stream }

open('/etc/shadow', read,S).
permission_error(open, source_sink, '/etc/shadow')
f)

representation_error(Flag)

A representation for some term is expected, but none can be provided by the system.

Flag ∈ { character, character_code, in_character_code, max_arity, max_integer, min_integer }

functor(F, f, 1000).
representation_error(max_arity).
In this case, we expected that f/1000 can be represented.
Systems whose max_arity is smaller than 1000 will produce this error.
g)

evaluation_error(Error)

An (error-free) evaluation is expected, but Error happened during evaluation.

Error ∈ { float_overflow, int_overflow, undefined, underflow, zero_divisor }

X is 1/0.
evaluation_error(zero_divisor).
h)

resource_error(Resource)

A resource is expected to be available, but it is not/not enough of it is left. length(L,L).
resource_error(stack).
i)

syntax_error(Imp_dep_atom)

Text of correct syntax is expected for a Prolog text, but some invalid syntax described by Imp_dep_atom was found instead. read(X). with "a b".
syntax_error(operator_expected).
j)

system_error

A functioning system was expected, but a malfunctioning system is present instead. Hopefully, this error never happens!
k)

uninstantiation_error(Culprit)

A term with property uninstantiation was expected, but the instantiated term Culprit was found instead. open('/dev/zero',read,S), open('/dev/null',read,S).
uninstantiation_error(imp_dep(2314))

Summary

In ISO's error classification, the error name describes what is expected but not found. This is described by referring to the category of the expected thing not the concrete term. I.e. a type_error expects a term of a type, not a type itself.

Candidates ordered by preference

The votes were collected between 2009-08 and the final discussion 2010-07-21.

Abstentions: 3.

uninstantiation_error pro: 11
If things can be uninstantiated, they are in a state of uninstantiation or have the property of uninstantiation. The adjective uninstantiated is very well known, so maybe also uninstantiation could become a notion? Occurrences of uninstantiated in 13211-1:1995:
3.193 uninstantiated: A variable is uninstantiated
when it is not instantiated.
3.96 instantiated: A variable is instantiated with re-
spect to a substitution if application of the substitution
yields an atomic term or a compound term.

A term is instantiated if any of its variables are instantiated.

3.146 read-option: A compound term with uninstanti-
ated * arguments which amplifies the results produced
by the built-in predicate read-term/3 (8.14.1) and the
bootstrapped * built-in predicates based on it (see 7.10.3).

Remark: In 7.10.3 the read-options are variables/1, variable_names/1, singletons/1.

Apart from above terminology entries (3), uninstantiated is used only once: note 3 of the description of bagof/3 (8.10.2.1).
freeness_error pro: 9
We expect a term with freeness in that place but a term that was bound to some nonvariable term was found instead.

+ The opposite of bound is free.

+ In 13211-1 free is used to refer to free variables of a term with respect to another term (7.1.1.4) - a notion needed for bagof/3 (8.10.2) and setof/3 (8.10.3). So the two uses would not collide.

- On the other hand, free variables are used in a slightly different meaning in 7.1.1.4

7.1.1.4 Free variables set of a term
The free variables set, FV, of a term T with respect to a
term V is a set of variables defined as the set difference
of the variable set (7.1.1.1) of T and BV where BV is a
set of variables defined as the union of the variable set of
V and the existential variables set (7.1.1.3) of T.
That is, it is insufficient to be a variable alone to be a free variable.

+ Freeness analysis is a well established notion in the context of (logic) program analysis. It determines which terms are variables at a certain point in time. Freeness analysis is clearly seen as being separate from aliasing analysis.

- The terms "free variable" and "bound variable" have their classical meaning in logic (e.g., all the variables in a clause are bound by the implicit quantifier), so overloading them within the context of logic programming appears problematic.

subinstantiation_error pro: 1
We expect an instantiation that is less than what an instantiation usually is. The name is preferable to underinstantiation_error.
noninstantiation_error
Can things be noninstantiated? Since we already use instantiation error.
unboundness_error
We expect a term with unboundness but a term bound to some term was found instead.
unbound_argument_error
range_error
This is the name used for similar situations in Quintus Prolog:
A range error occurs when an output argument was supplied with an illegal value. This is similar to a type error or a domain error, except that it is a hint that a variable would be a good thing to supply instead; type and domain errors are associated with input arguments, where a variable would usually not be a good idea. The exception code associated with a range error is range_error(Goal, ArgNo, TypeName, Culprit) This has the same arguments as a type error. Most built-in predicates do not raise any range errors. Instead they fail quietly when an output argument fails to unify.
Note that the situation in Quintus is slightly different to the situation of unexpected instantiations. This can be seen best by considering the errors for the Quintus predicate asserta/2. If the reference happens to be the same, no error is produced. While this is quite improbable it is still possible to guess a reference a priori. Or to print out a reference and reuse it in another system. In any case, the Quintus error depends on the success when unifying a term and not only on the instantiation.
asserta(+Clause, -Ref)
Ref
<db_reference> a database reference which uniquely identifies the newly asserted Clause.
Description:
  • Ref should be uninstantiated; a range exception is signalled if Ref does not unify with its return value. This exception is signalled after the assert has been completed.
Exceptions:
  • range_error if Ref does not unify with the returned database reference.
The name range collides with the following uses in 13211-1:1995:
3.22 byte: An integer in the range [0..255]...
6.3.4 Compound terms - operator notation
...
The priority of an operator is an integer in the range R, where
R = {r, rZ | 1 ≤ r ≤ 1200}
7.11 Flags
...
Each flag has a permitted range of values; any other
value is a Domain Error (7.12.2 c).
8.17.1.1 set_prolog_flag/2 ...
a) Associates Value with the flag Flag (7.11), where
Value is a value that is within the implementation
defined range of values for Flag,
The name collides with the first CD (N92 1992-03) 7.11.11 Range error, which closely corresponds to a domain error.

The name collides with ECLiPSe-Prolog's synonymous Domain Error.

anti_instantiation_error
The opposite of the instantiation_error. But do we want to say that an anti instantiation is expected?
uniqueness_error
Promises too much. How shall we guarantee that the variable is unique? And why should not a constant like 1 be considered unique?
unboundedness_error
We expect a term with unboundedness but a term bounded to some term was found instead.
variableness_error
variable_error
We expect a variable in that place. This is true for all situations we are aware of. However, the name does not fit entirely into the existing scheme: The instantiation error isn't called instantiated_term_error; the type error isn't called typed_term_error. Yet, both errors actually look for such terms.

Also, the name itself might be easily misunderstood, as this might suggest that the error is changeable. (I.e. variable ≈ changeable). Further, what we actually want is not only a free variable (in 13211-1 a variable consistently means a free variable), but a free and unaliased variable.

freevar_error
freesubvar_error
underinstantiation_error
This notion is even used in some publications. The alternate spelling under_instantiation seems to be problematic, as this can be easily confused with the frequently used locution "goal under instantiation".
distinctness_error
cessation_error
What is the opposite of instantiation? This is an attempt to construct the opposite by going back to Latin: instantiation, [the actual root is:] instare/3, [the opposite might be] cedere/3, [the noun] cessation.
noaliasing_error
As a special case, if two free and distinct variables are expected.
vacuity_error
unbound_error
This is an adjective and not a noun describing the expected property. Also, it has the same problem as variable_error. So unbound_error implies: this error is unbound - whatever that means.
representation_error(variable)
We expect to represent the argument as a variable but the (nonvariable) term cannot be represented as a variable. This is semantically less problematic than the original type_error(variable, Culprit) as it does not imply a semantic failure.

On the other hand representation errors shall be issued if an implementation defined limit has been breached (7.12.2 f). The promise behind is that in principle this error would vanish if the implementation improves. This is the case for integers: By going from a 32-bit system to a 64-bit system a lot of representation errors will vanish for a processor with bounded integers. Similarly for character codes going from ASCII to more. However, representation_error(variable) will never vanish as this is not a limit in the implementation. So the promise behind will never be fulfilled.

misinstantiation_error
Not very specific. Also the Instantiation error could be considered a Misinstantiation error.
malinstantiation_error
malbinding_error
over_instantiation_error
This name describes the found term and not what we expect. So it does not fit into the existing naming scheme for errors.
argument_already_bound_error
overbinding_error
hyperbinding_error
hyperinstantiation_error

Current status

Some Prolog systems already changed errors to semantically less problematic terms.
Validated HTML