5

The question

I have a question related to logical purity.

Is this program pure?

when(ground(X), X > 2).

Some [ir]relevant details about the context

I'm trying to write pure predicates with good termination properties. For instance, I want to write a predicate list_length/2 that describes the relation between a list and its length. I want to achieve the same termination behaviour as the built-in predicate length/2.

My question seeks to find if the following predicate is pure:

list_length([], 0).
list_length([_|Tail], N):-
    when(ground(N), (N > 0, N1 is N - 1)),
    when(ground(N1), N is N1 + 1),
    list_length(Tail, N1).

I can achieve my goal with ...

:- use_module(library(clpfd)).
:- set_prolog_flag(clpfd_monotonic, true).

list_length([], 0).
list_length([_|Tail], N):-
    ?(N) #> 0,
    ?(N1) #= ?(N) - 1,
    list_length(Tail, N1).

... or I can use var/1, nonvar/1 and !/0, but then is hard to prove that the predicate is pure.

list_length([],0).
list_length([_|Tail], N):-
    nonvar(N), !,
    N > 0,
    N1 is N - 1,
    list_length(Tail, N1).
list_length([_|Tail], N):-
    list_length(Tail, N1),
    N is N1 + 1.
Community
  • 1
  • 1
Tudor Berariu
  • 4,910
  • 2
  • 18
  • 29
  • 3
    I have rather a question: where is 'purity' defined / described ? – CapelliC Jan 17 '15 at 16:31
  • 2
    @CapelliC: See **6 Pure Prolog** of The Art of Prolog. – false Jan 17 '15 at 18:06
  • 1
    I have a question, too: what's the `?(N)` notation mean? I haven't seen it anywhere else. – lurker Jan 18 '15 at 04:05
  • 1
    This is how variables that occur in arithmetic expressions should be used when the `clpfd_monotonic` flag is true. [here](http://www.swi-prolog.org/pldoc/man?section=clpfd-advanced-topics) – Tudor Berariu Jan 18 '15 at 07:32

1 Answers1

4

Logical purity of when/2 and ground/1

Note that there is the ISO built-in ground/1 which is just as impure as nonvar/1. But it seems you are rather talking about the conditions for when/2. In fact, any accepted condition for when/2 is as pure as it can get. So this is not only true for ground/1.

Is this program pure?

when(ground(X), X > 2).

Yes, in the current sense of purity. That is, in the very same sense that considers library(clpfd) as pure. In the very early days of logic programming and Prolog, say in the 1970s, a pure program would have been only one that succeeds if it is true and fails if it is false. Nothing else.

However, today, we accept that ISO errors, like type errors are issued in place of silent failure. In fact, this makes much more sense from a practical point of view. Think of X = non_number, when(ground(X), X > 2 ). Note that this error system was introduced relatively late into Prolog.

While Prolog I reported errors of built-ins explicitly1 the subsequent DEC10-Prolog (as of, e.g. 1978, 1982) nor C-Prolog did not contain a reliable error reporting system. Instead, a message was printed and the predicate failed thus confusing errors with logical falsity. From this time, there is still the value warning of the Prolog flag unknown (7.11.2.4 in ISO/IEC 13211-1:1995) which causes the attempt to execute an undefined predicate to print a warning and fail.

So where's the catch? Consider

?- when(ground(X), X> 2), when(ground(X), X < 2).
   when(ground(X), X>2), when(ground(X), X<2).

These when/2s, while perfectly correct, now contribute a lot to producing inconsistencies as answers. After all, above reads:

Yes, the query is true, provided the very same query is true.

Contrast this to SICStus' or SWI's library(clpfd):

?- X #> 2, X #< 2.
   false.

So library(clpfd) is capable of detecting this inconsistency, whereas when/2 has to wait until its argument is ground.

Getting such conditional answers is often very confusing. In fact, many prefer in many situations a more mundane instantiation error to the somewhat cleaner when.

There is no obvious general answer to this. After all, many interesting theories for constraints are undecidable. Yes, the very harmless-looking library(clpfd) permits you to formulate undecidable problems already! So we will have to live with such conditional answers that do not contain solutions.

However, once you get a pure unconditional solution or once you get real failure you do know that this will hold.

list_length/2

Your definition using library(clpfd) is actually slightly better w.r.t. termination than what has been agreed upon for the Prolog prologue. Consider:

?- N in 1..3, list_length(L, N).

Also, the goal length(L,L) produces a type error in a very natural fashion. That is, without any explicit tests.

Your version using when/2 has some "natural" irregularities, like length(L,0+0) fails but length(L,1+0) succeeds. Otherwise it seems to be fine — by construction alone.


  1. The earliest account is on p.9 of G. Battani, H. Meloni. Interpréteur du langage de programmation Prolog. Rapport de D.E.A. d'informatique appliquée, 1973. There, a built-in in error was replaced by a goal that was never resolved. In current terms plus/3 of II-3-6 a, p.13 would be in current systems with freeze/2:
plus(X, Y, Z) :-
       (  integer(X), integer(Y), ( var(Z) ; integer(Z) )
       -> Z is X+Y
       ;  freeze(_,erreur(plus(X,Y,Z)))
       ).

So plus/3 was not "multi-directional".

false
  • 10,264
  • 13
  • 101
  • 209
  • There is this [answer](http://stackoverflow.com/a/13760173/1478624) where @mat says that `when/2` is not pure. I just want to be sure I got it right. What he meant is that programs that use `when/2` are not guaranteed to be pure, is that correct? You said that all conditions for `when/2` are pure. That leads to one conclusion: `when(Condition, Goal)` is a pure program if `Goal` is pure. – Tudor Berariu Jan 19 '15 at 17:12
  • 1
    @Tudor: There is a bit more to this. Your uses (so far :-)) are fine. In particular, since the only variables that appear outside of `when/2` are also found in the first argument, or are otherwise not problematic. But what, if the goal and the guard are completely unrelated? In this manner you can do a lot of strange things that are not necessarily pure. `when(nonvar(X), p(Y))` this `Y` is it now constrained or not? In current implementations it appears as if it would be an unconstrained variable - that's how the goto analogy comes into place. You can make extremely brittle code that way. – false Jan 19 '15 at 17:30
  • It might be interesting to study some restrictions on the variables and predicates that are allowed for the second argument in order to provide a pure `when/2`. I remember from some of your answers in other contexts that programs written using this postponed goals were studied some time ago and no great advantage of this style has been found. But, in some cases (like when one wants to have the goals executed in a context-dependent order) they might be useful. – Tudor Berariu Jan 19 '15 at 20:09