3

I'm writing some Peano arithmetic to better learn Prolog. The following is the code I've come up with, and it seems equivalent of what I've seen elsewhere online:

add(X,z,X).
add(X,s(Y),s(Z)) :- add(X,Y,Z).
mult(_,z,z).
mult(X,s(Y),W) :- mult(X,Y,Z), add(X,Z,W).

However if I try to make a simple query, like the divisor pairs of 0, I run into problems:

| ?- mult(X,Y,z).

Y = z ? ;

X = z
Y = s(z) ? ;

X = z
Y = s(s(z)) ? ;

X = z
Y = s(s(s(z))) ? ;

Fatal Error: global stack overflow (size: 32768 Kb, reached: 32765 Kb, environment variable used: GLOBALSZ)

It really bugs me, as to how can it get all the way to Y = 3, but not to Y = 4?

false
  • 10,264
  • 13
  • 101
  • 209
Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
  • 1
    it's a really weird behaviour, and difficult to track down... I wonder if the arity could be correlated to the bug. – CapelliC Sep 27 '13 at 15:23

2 Answers2

3

The stack overflow occurs because, for your query, the predicate add/3 is eventually called with a variable as the middle argument. When you backtrack into it, you get a loop that leads to a stack overflow. Consider the call add(X,Y,Z). The first clause gives you a first solution, add(X,z,X). But, on backtracking, when you use the second clause, you unify your query with add(X,s(Y),s(Z)) and recursively call add(X,Y,Z), back to where you started (remember, the middle argument is not instantiated, so Y in s(Y) will also not be instantiated on the call. You're able to get the first four solutions, as you show above, only thanks to the base cases of both predicates. When usage of those base clause (on backtracking) is exhausted, you get into the loop I just explained above.

Try add the following clause as the first clause of the add/3 predicate:

add(X,Y,Z) :-
    write('Called: '), writeq(add(X,Y,Z)), nl, fail.

Retrying the query you will get (hope you are quick with Control-C):

| ?- mult(X,Y,z).

Y = z ? ;
Called: add(_279,z,z)

X = z
Y = s(z) ? ;
Called: add(_279,z,_307)
Called: add(_279,_279,z)

X = z
Y = s(s(z)) ? ;
Called: add(_279,z,_309)
Called: add(_279,_279,_309)
Called: add(z,z,z)

X = z
Y = s(s(s(z))) ? ;
Called: add(s(_307),_307,_309)
Called: add(s(z),s(s(z)),z)
Called: add(s(s(_311)),_311,_313)
Called: add(s(s(z)),s(s(s(s(z)))),z)
Called: add(s(s(s(_315))),_315,_317)
Called: add(s(s(s(z))),s(s(s(s(s(s(z)))))),z)
Called: add(s(s(s(s(_319)))),_319,_321)
Called: add(s(s(s(s(z)))),s(s(s(s(s(s(s(s(z)))))))),z)
Called: add(s(s(s(s(s(_323))))),_323,_325)
...

Hope this helps.

Paulo Moura
  • 18,373
  • 3
  • 23
  • 33
  • 2
    An additional note: if you look closely to the start of the endless printouts above, you will notice that you are trying to add increasing values of the first two arguments of `add/3`, checking if they add up to `z`. The base clause, however, never applies... – Paulo Moura Sep 27 '13 at 17:14
  • 1
    That's an impressive analysis! Prologs inner workings are so simple, but the way they show themselves can be mind boggling. :) What do you think would be the best way to fix the situation? I can add a predicate that says X and Y should be less than or equal to Z, but should I put it in add or mult? – Thomas Ahle Sep 28 '13 at 18:54
1

I know that this is a very old post, but I've just started learning Prolog and find the question fascinating. So here's my two cents.

I've noticed that if you change your rule

mult(X,s(Y),W) :- mult(X,Y,Z), add(X,Z,W).

to a disjunction

mult(X,s(Y),W) :- mult(X,Y,Z); add(X,Z,W).

and run the same query

?- mult(X,Y,z).

You'll get some results, but as you can see the SWI Prolog interpreter doesn't show much detail after a point:

?- mult(X,Y,z).
Y = z ;
Y = s(z) ;
Y = s(s(z)) ;
Y = s(s(s(z))) ;
Y = s(s(s(s(z)))) ;
Y = s(s(s(s(s(z))))) ;
Y = s(s(s(s(s(s(z)))))) ;
Y = s(s(s(s(s(s(s(z))))))) ;
Y = s(s(s(s(s(s(s(s(z)))))))) ;
Y = s(s(s(s(s(s(s(s(s(z))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) .

Doing the same in Gnu Prolog looks much better, to put it gently:

| ?- mult(X,Y,z).

Y = z ? ;

Y = s(z) ? ;

Y = s(s(z)) ? ;

Y = s(s(s(z))) ? ;

Y = s(s(s(s(z)))) ? ;

Y = s(s(s(s(s(z))))) ? ;

Y = s(s(s(s(s(s(z)))))) ? ;

Y = s(s(s(s(s(s(s(z))))))) ? ;

Y = s(s(s(s(s(s(s(s(z)))))))) ? ;

Y = s(s(s(s(s(s(s(s(s(z))))))))) ? ;

Y = s(s(s(s(s(s(s(s(s(s(z)))))))))) ? ;

Y = s(s(s(s(s(s(s(s(s(s(s(z))))))))))) ? ;

Y = s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))) ? ;

Y = s(s(s(s(s(s(s(s(s(s(s(s(s(z))))))))))))) ? ;

Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))))) ? ;

Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(z))))))))))))))) ? ;

Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))))))) ? ;   
...

In SWI Prolog (and GNU Prolog) you can call the debugger which illustrates a bit more the excellent theoretical analysis of Paulo Moura, regarding the original problem:

 ?- trace, mult(X,Y,z).

As you will see, it seems to be running for ever, so that would explain the stack overflow. See the result of the trace here, in all its glory. You can trace the infinite loop on your browser as well, by running the above trace.

milia
  • 521
  • 8
  • 20
  • 1
    Foremostly, your new definition is too general. `mult(s(s(z)),s(z),s(s(s(z)))).` succeeds. That is 2x1=3. – false Jan 29 '17 at 22:46
  • I appreciate the feedback, especially when it comes from someone more knowledgeable. I should have looked more into Peano axioms before replying. Thanks ! – milia Jan 31 '17 at 22:58