Standard term order (ISO/IEC 13211-1 7.2 Term order) is defined over all terms — including variables. While there are good uses for this — think of the implementation of setof/3, this makes many otherwise clean and logical uses of the built-ins in 8.4 Term comparison a declarative nightmare with imps (short form for imperative constructs) all around. 8.4 Term comparison features:
8.4 Term comparison
8.4.1 (@=<)/2, (==)/2, (==)/2, (@<)/2, (@>)/2, (@>=)/2.
8.4.2 compare/3.
8.4.3 sort/2.
8.4.4 keysort/2.
To give an example, consider:
?- X @< a.
true.
This succeeds, because
7.2 Term order
An ordering term_precedes (3.181) defines whether or
not a termXterm-precedes a termY.If
XandYare identical terms thenXterm_precedesY
andYterm_precedesXare both false.If
XandYhave different types:Xterm_precedesYiff the
type ofXprecedes the type ofYin the following order:
variableprecedesfloating pointprecedesinteger
precedesatomprecedescompound.NOTE — Built-in predicates which test the ordering of terms
are defined in 8.4.
...
And thus all variables are smaller than a. But once X is instantiated:
?- X @< a, X = a.
X = a.
the result becomes invalid.
So that is the problem. To overcome this, one might either use constraints, or stick to core behavior only and therefore produce an instantiation_error.
7.12.2 Error classification
Errors are classified according to the form of
Error_term:a) There shall be an Instantiation Error when an
argument or one of its components is a variable, and an
instantiated argument or component is required. It has
the forminstantiation_error.
In this manner we know for sure that a result is well defined as long as no instantiation error occurs.
For (\==)/2, there is already either dif/2 which uses constraints or dif_si/2 (formerly iso_dif/2) which produces a clean instantiation error.
dif_si(X, Y) :-
X \== Y,
( X \= Y -> true
; throw(error(instantiation_error,dif_si/2))
).
So what my question is about: How to define (and name) the corresponding safe term comparison predicates in ISO Prolog? Ideally, without any explicit term traversal. Maybe to clarify: Above dif_si/2 does not use any explicit term traversal. Both (\==)/2 and (\=)/2 traverse the term internally, but the overheads for this are extremely low compared to explicit traversal with (=..)/2 or functor/3, arg/3.