6

I'm trying to understand how Prolog works. I'm using SWI-Prolog. Here's a bit of code:

forall(C1,C2) :- \+ (C1, \+ C2).

foo(N) :- N < 10.

bar(N) :- N > 5.

foobar(N) :- forall(foo(N),bar(N)).

It produces desired output if I do something like:

?- foobar(5).
false.

But when I try to see all the possible values I get an error:

?- foobar(N).
ERROR: </2: Arguments are not sufficiently instantiated

What is going on here?

8
  • 4
    The </2 and >/2 operators require that all arguments have specific values (are instantiated) in order to work. So if N doesn't have a value (is not instantiated) then N < 10 will generate that error. If you're trying to generate possible values with certain constraints, you might want to use the CLPFD library, then you can use N #< 10, etc.
    – lurker
    Commented Mar 18, 2015 at 19:07
  • 1
    What do you intend to mean by it?
    – false
    Commented Mar 18, 2015 at 19:16
  • 1
    @mat: Before going against instantiation errors that effectively do not claim anything wrong, what about going against programs that produce incorrect answers first?
    – false
    Commented Jul 26, 2015 at 22:12
  • 1
    @false: In my view, there are way too many questions of this type on this site, and most of them can be trivially solved by simply using more declarative predicates. I want to reward users who propagate more elegant methods, typically giving more correct and also more general solutions. 150 points is a first sign of appreciation to encourage this. I give 200 points and more for more significant contributions, such as very elegant solutions.
    – mat
    Commented Jul 27, 2015 at 11:12
  • 1
    And apparently having to justify the 3rd already. It's not at all fun to give bounties this way.
    – mat
    Commented Jul 27, 2015 at 11:42

2 Answers 2

3
+150

Basically you are writing a program the check for a global implication:

forall N, if N < 10 then N > 5

and you want to know for which domain of N that holds.

Now you correctly rewrite that in prolog as:

\+ ( N < 10, \+ ( N > 5 ) ).

But if you try to give that query to the prolog interpreter, it will output the error:

?- \+ ( N < 10, \+ ( N > 5 ) ).
ERROR: </2: Arguments are not sufficiently instantiated

because the arguments of < are not instantiated. It happens the same thing with a simple query such as N < 3. Of course, if you instantiate it before the query, the problem goes away:

?- N=5, \+ ( N < 10, \+ ( N > 5 ) ).
false.

(the statement does not hold for N=5)

?- N=6, \+ ( N < 10, \+ ( N > 5 ) ).
N = 6.

(but it holds for N=6).

You could also put a predicate that generates multiple assignments for N via backtracking, in place of that N=6:

?- between(1, 12, N), \+ ( N < 10, \+ ( N > 5 ) ).
N = 6 ;
N = 7 ;
N = 8 ;
N = 9 ;
N = 10 ;
N = 11 ;
N = 12.

but for a large domain, that is going to be highly inefficient (it will require backtracking for every element of the domain).

If you want to reason about finite domains (i.e. integers), you can use the CLPFD library as @lurker suggested. This approach is more efficient, because it has rules for reasoning about intervals, intersection on intervals, and many more.

You have to replace <, >, ,, \+ with the CLP operators #<, #>, #/\, #\. Let's try it:

?- use_module(library(clpfd)).
?- #\ ( N #< 10 #/\ #\ ( N #> 5 ) ).
N+1#=_G11193,
N#>=6#<==>_G11205,
10#>=_G11193#<==>_G11217,
_G11217 in 0..1,
_G11217#/\_G11244#<==>0,
_G11244 in 0..1,
#\_G11205#<==>_G11244,
_G11205 in 0..1.

that might be a bit hard to read, but among many other things, it is telling you the answer you are looking for, that is the domain of N: N #>= 6.

2

TL;DR. Prolog has nice algebraic properties as long as you stick to its logically-pure core.


For details, read the following excellent answers to Prolog questions on StackOverflow:

Not the answer you're looking for? Browse other questions tagged or ask your own question.