Recently, SWI 7 has been released as a successor to SWI-Prolog 6. After discussions, a document titled SWI-Prolog future directions henceforth called "Future Directions" has been issued. The document refers to "the ISO standard" right from the first sentence on and requires a detailed reply, since the ISO standard, its standardization process, as well as SWI7's relation to it is presented in a misleading and inaccurate manner.
ISO Prolog resulted out of a process which begins in 1984; formally started in 1987 after 21 nations approved the "New Work Item programming language Prolog". More than 120 individuals contributed to this project with Roger Scowen as convener and editor. It was published as an International Standard in 1995. It is maintained by a working group (ISO/IEC JTC1 SC22 WG17) with two published technical corrigenda so far.
ISO Prolog is a voluntary standard, like most International Standards, meaning that it serves as a precise reference that everyone can adopt or not. Vendors and providers conforming to ISO Prolog will announce this via self-declaration, often in the very first sentence to ease procurement formalities; contracts refer to it similarly.
A system like SWI7 is free to decide whatever it wants with respect to ISO Prolog. However, the statements in "Future Directions" misrepresent the current state with respect to ISO.
"Future directions" concedes that SWI-Prolog never followed ISO errors very closely which is accurate. However, this is in no way as important as the other changes to SWI7. Namely, the change of canonical syntax which makes term syntax incompatible to other systems and also previous versions of SWI-Prolog.
However, SWI7 can no longer read ISO conforming canonical syntax.
This means that terms written by an ISO Prolog system can no longer be
read by SWI7. Not even terms written by SWI-Prolog 6 can be read by
SWI7. This means that logfiles or other data that have been generated
with write_canonical/1
in the past and in the hope to be
readable in the future, cannot be read reliably in SWI7. An example
of a term in canonical syntax that can no longer be read by SWI7:
?- writeq('.'(a,[])).
ERROR: Type error: `dict' expected, found `a'
Expected: [a]
.
Also, SWI7 no longer writes canonical syntax but a proper version of it which may again be a source of instability when a reader in another language is written to read canonical syntax only. So while the written Prolog text is (at least sometimes) valid Prolog text, it is not conforming canonical syntax.
To repeat, canonical syntax was meant as a stable, reliable data format. This is no longer the case in SWI7. Adding options for "traditional" behavior does not improve ISO conformance.
Adding a conforming infix dot. The actual motivation for the changes in SWI7 seems to be the desire to distinguish lists and the (.)-notation. This could be achieved in an ISO conforming manner by adding the following extension instead: In case there is no current op-declaration for an operator . and the dot does not serve as operand: interpret dot as an infix operator that maps to '$dot'/2 or anything else. In this manner the canonical syntax remains unaffected.
With such an extension the following would hold:
?- '.'(E,L) = [1,2]. E = 1, L = [2]. ?- .(E,L) = [1,2]. E = 1, L = [2]. ?- op(400,xfy,.). % That is: turn off special meaning of . true. ?- A.B = [1,2]. A = 1, B = [2].
It is a basic assumption for code traversing terms that nonvar
terms can be analyzed with functor/3
or (=..)/2
. This is no longer the case in SWI7. The
SWI7-specific term f()
cannot be analyzed.
?- catch(functor(f(),F,A),error(E,_),true).
E = domain_error(compound_non_zero_arity, f()).
?- catch(functor(f,F,A),error(E,_),true).
F = f,
A = 0.
The domain error produced is quite misleading too, it claims
that functor/3
expects a "compound_non_zero_arity". But
also atoms are admitted that are not of this domain.
Some other differences are documented in SWI7.
"Future directions" also criticizes ISO Prolog directly. Here are answers to some of them.
It is accurate that error handling in ISO Prolog built-in predicates is more complex than in, say, Javascript. The reason is the very different, relational, nature of Prolog.
In SWI, some errors appear, some not, and sometimes unspecified errors appear. The programmer is forced to do experiments and hope that the next version of SWI does not change the assumptions made. Also, proving program properties becomes a very difficult venture. Ultimately, for ensuring diligence, a programmer has to write a proper validation suite. Or look at collections of examples to "understand" a specific predicate. Should we really make such tables for each and every predicate?
It is correctly observed, that in
ISO, call((fail,1))
must produce a type error. However,
the reader is not informed why such seemingly erratic behaviour is
required. The reason is the precise handling of Prolog's control
structures, like cut, disjunction, if-then, if-then-else and the
meta-call. Prior to ISO, there was no agreed upon way how to handle
these control structures in the general case which led to all kinds of
inconsistencies and bugs. ISO has given these constructs a precise
meaning. There is no ambiguity whatsoever how control constructs are
interpreted in ISO. Implementations that do not follow the standard
by the letter do expose different behavior. And SWI does not comply
fully - contrary to the claim made in "Future directions". As an
example reported years ago,
call((fail,\+1))
shall fail, but produces an error in
SWI. Further, the meaning of control constructs depends also on the
libraries that have been loaded before term-to-body conversion
(7.6.2). Here is a minimal example in SWI. While nobody writes such
code directly, similar constructs may be produced during goal or term
expansions.
?- call((X=!,once((X,fail;true)))).
false.
?- use_module(library(apply_macros)).
% messages omitted
true.
?- call((X=!,once((X,fail;true)))).
X = !.
In general, SWI rather omits errors. The goal
sort([],[t|1]) /* 8.4.3.3 c */ ; keysort([], [_|1]) /* 8.4.4.3 c */ ; a =.. [t|1] /* 8.5.3.3 b */ ; term_variables(_,[t|1]) /* 8.5.5.3 a */ ; findall(t,true,[t|1]) /* 8.10.1.3 c */ ; bagof(t,true,[t|1]) /* 8.10.2.3 c */ ; setof(t,true,[t|1]) /* 8.10.3.3 c */ ; atom_chars(a, [_|1]) /* 8.16.4.3 c */ ; number_chars(2,[_|1]) /* 8.16.7.3 c */ ; false.fails in SWI. But each single goal should produce a type error.
Another ISO conforming option is to merge transparently the (internal) representation of chars and strings. Yet another option (maybe the simplest one) is to merge strings and atoms. All three approaches do not need to break with ISO Prolog. SWI7 chose a path of incompatibility and even foresees a further change by introducing a separate data type for characters — in addition to ISO's character type as atoms of length 1.
How to make SWI7 strings a bit less non-conforming.
The best one can do within SWI7 is
to set_prolog_flag(double_quotes, chars)
and set_prolog_flag(back_quotes, string)
. In this manner
SWI7's write_canonical/1
writes strings with backquotes
as a conforming extension would do.
?- atom_string(abc,S), write_canonical(S). "abc" S = "abc". ?- set_prolog_flag(double_quotes, chars). true. ?- set_prolog_flag(back_quotes, string). true. ?- atom_string(abc,S), write_canonical(S). `abc` S = "abc".Not all cases are covered, though. Says Richard O'Keefe:
What are we to do about Prolog systems where strings are written using double quotes? Wince, regret the JavaScript envy, and request a mode that is more Prolog-like.
'\141\'
and '\x61\'
(both representing in ASCII the
atom a
) idiosyncratic. However, The ISO process is a very flexible and fast process. But to run at maximal speed it requires commitment, cooperation, and conformance.