1
?- X in 1..100.
X in 1..100.

?- X #> 0, X #< 101.
X in 1..100.

So far so good!

Now let's imagine that the sup bound of X depends on Y in 100..200.

?- Y in 100..200, X #> 0, X #=< Y.
Y in 100..200,
Y#>=X,
X in 1..200.

The domain of X propagates correctly to 1..200, with the constraint between X and Y.

But…</p>

?- Y in 100..200, X in 1..Y.
ERROR: Arguments are not sufficiently instantiated

I thought that in was equivalent to the inequalities declaration, but it apparently is not.

Is there any hidden reason as to why in requires that both bounds are ground?

4

1 回答 1

4

That's a very good question. It has a straight-forward answer that may at first appear deep, yet is really easy to understand once you are familiar with declarative debugging approaches that are beginning to become more widely available.

Declarative debugging relies on properties of logic programs that we know always hold. A key property of pure logic programs (see ) is monotonicity:

Generalizing a goal can at meast increase the set of solutions.

Here is an example:

?- append([a], [b], [c]).
false.

Why does this fail? We want to find an actual cause of failure, an explanation why this does not succeed. It is tempting to think in procedural terms and trace the execution, but this quickly gets extremely complex and does not really show us the essence of the reason.

Instead, we can get to the essence by generalizing the goals systematically. For example, the following succeeds:

?- append([a], [b], Cs).

I have generalized the query by replacing the term [c] with Cs, which subsumes it.

You can do this automatically, see Ulrich Neumerkel's library(diadem) and especially the GUPU system where these ideas are fully developed and help tremendously to locate the precise location of mistakes in Prolog programs.

To apply such techniques to their utmost extent, you must aim to preserve as many interesting properties as possible in your programs, essentially by staying in the pure monotonic core of Prolog. I say "essentially" because these techniques also work somewhat beyond this subset on a class of programs that is very hard to classify formally. Note also that much of the ongoing work on logic programming is aimed towards increasing the pure monotonic subset of Prolog as far as possible, so the restriction becomes less and less severe over time.

Consider now the specifics of (in)/2: The domain argument of (in)/2 carries within it a declarative flaw in that it uses a defaulty representation of domains. There are several ways to see this, and notably from the following situation: Suppose I tell you that a domain has the form inf..High, then what is High? It can be either of:

  • sup
  • an integer.

The fact that we cannot clearly distinguish these cases tells you that this representation is defaulty.

A clean representation of boundaries would look like this:

  • inf, sup to denote the infinities
  • n(N) to denote the integer N.

With such a representation, I could tell you that the domain looks like this: inf..n(N), and you know that N must be an integer.

Now another example:

?- X in 1..0.
false.

Why does this fail? Let us again generalize the arguments to find a reason:

?- X in Low..0.

Suppose now that (in)/2 accepted this as an admissible query. Then what is Low? Again, it can be either an integer, or the atom inf. Unfortunately, there is no sensible way to constrain Low in this way: Constraining it to integers would make CLP(FD) constraints most appropriate, making it tempting to answer with:

X in Low..0,
Low in inf..0

but that is of course too much, because it precludes Low = inf:

?- Low in inf..0, Low = inf.
Type error: `integer' expected, found `inf' (an atom)

Knowing that we cannot decide the type of the term Low in such situations, and lacking a sensible way to state this, an instantiation error is raised to indicate that such a generalization is inadmissible.

From these considerations, the overall answer is really simple:

(in)/2 cannot be easily generalized due to the defaulty representation of domains.

Invariably, there are only two reasons for using such representations:

  • they are easier to type
  • they are easier to read.

The hefty drawback of course is that they stand massively in the way of declarative reasoning over your programs.

Note that internally, the CLP(FD) system of SWI-Prolog does use a clean representation of domains. This can also be carried over to the user side, so that we could write:

?- X in n(Low)..0.

with the declaratively perfectly valid answer:

X in n(Low)..0,
Low in inf..0.

Over time, such cleaner notations will definitely find their way to users. For now, it would be a bit too much to ask, at least in my experience.

In the specific examples you cite, the domain boundary is already constrained to integers, so of course we can distinguish the cases and translate this automatically to inequalitites. However, this would not remove the core problem of defaultyness, and exchanging the goals would still raise an instantiation error.

于 2016-09-20T12:03:27.310 回答