SWI7 and ISO Prolog

Ulrich Neumerkel, editor of ISO/IEC 13211-1 (core).
2013-12-04 First announcement (list,copy)
2013-12-08 Adding dot-notation in an ISO conforming manner (list,copy)
2014-09-23 Add remark on non-conformance of SWI7's strings
2015-09-10 How to make SWI7 strings a bit less non-conforming

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.

What is ISO Prolog?

ISO Prolog is the language described in ISO/IEC 13211-1:1995 (core) including Cor.1:2007, Cor.2:2012 and ISO/IEC 13211-2:2000. I will refer to IS 13211-1 (core) only, since I am its editor. References to the codex are highlighted.

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.

SWI7 did give up its rudimentary ISO compliance

SWI7 changes the meaning of existing language syntax and semantics compared to SWI-Prolog 6. Neither SWI7 nor SWI-Prolog 6 are ISO compliant. But SWI7 changes key properties in the language. These changes are not an extension to ISO Prolog. There are ways to extend ISO Prolog such that existing conforming programs and data are not affected. See 5.5 Extensions. But the changes in SWI7 are a clear departure from 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.

SWI7 breaks canonical syntax

Low overhead canonical syntax is one of the key properties of ISO Prolog syntax. In this manner terms can be "serialized", stored or transmitted to other Prolog systems independently of the operator declarations.

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].

General term analysis no longer works with functor/3

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.

Error handling

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.

To cope with these complexities, many error messages are derived from a compact "Template and mode" description. One of the biggest advantages of ISO's consistent error handling is that a programmer can understand most of it by looking at that definition, and understand everything by looking at the errors subclause. Also, for program analysis, the situation is clear. As an example, consider keysort/2. There are six error clauses 8.4.4.3 a up to f. A functional or imperative language would only need two: b and e.

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?

Errors of call/1

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 = !.

Errors of length/2

For length/2, the list argument might either be checked or unchecked. Both possibilities have their advantages and the differences are minor. However, there is a big difference if all Prolog systems behave the same way, or differ according to the current taste of an implementer. WG17 did deliberate both cases, did consider the history of the predicate, and various use cases and finally made a decision. Now, we have at least 5 systems that conform, and some (including SWI) that do it differently. It is exactly this kind of superfluous diversity that prevents synergy between systems.

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.

Representing text

A separate string type is one option to address issues of improved text representation. It is even explicitly mentioned in the standard as a possible extension. Yet "Future Directions" presents this as a "revival of the BSI Prolog standard" which cannot be, for BSI produced drafts only, never a standard. Nevertheless, SWI7's string extension does not conform since SWI7's strings do not possess a canonical write syntax.

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.

Syntax

The document calls ISO syntax for octal and hexadecimal escape sequences like '\141\' and '\x61\' (both representing in ASCII the atom a) idiosyncratic. However, 9 10 out of 10 systems recognize this notation correctly. See #108. If there is anything idiosyncratic in this context, it is the frequently changing extensions to escape sequences in SWI.

SWI7 and education

One very frequently mentioned requirement for realizing Prolog courses is a uniform set of predefined predicates. This would make the life of teachers much simpler. No need to change the material when going from one implementation to another. No hassles when a student has a more recent version installed. The Prolog prologue was started with this in mind too. However, SWI seems to prefer to differ. Also, the statement about solving "the N-queens problems elegantly and in splendid isolation" is irritating. This is an insightful example to experience different labeling strategies in beginners' courses. And it helped other Prolog systems to identify and remove bugs in their systems thereby improving both the academic and industrial experience. A robust and reliable implementation should have no problems with such examples in any case. This is really the first time I see such derogatory remarks by an implementer.

Is the ISO process really slow?

In passing, "Future Directions" remarks that the process to enlarge the (ISO) Prolog language is "too slow". This now turns things upside down! Since 1995, 13211-1 is present. Yet, 18 years later, SWI has not fully adopted it. And for many years, SWI ignored ISO completely. Only recently a visible move was taken (2007-2010). SWI's syntax is still far away from complete conformity. Also, in other areas, SWI needed literally years to adapt to marginal changes. And sometimes refuses them altogether.

The ISO process is a very flexible and fast process. But to run at maximal speed it requires commitment, cooperation, and conformance.

Conclusion

SWI7 is a radical departure from ISO Prolog and its ancestor Edinburgh Prolog. SWI7 is still "a Prolog", there is no doubt — but a very different one. In other programming languages such radical changes have been reflected by changing the name of the language.
ISO Prolog works, Validated HTML