[comp.lang.prolog] tidying up control

marcel@MERIDIAN.ADS.COM (Marcel Schoppers) (07/14/88)

I haven't kept up with attempts to clean up search control in Prolog, but
let me throw out an idea and see what happens.

Control in Prolog, when used around single predicates, comes in a few common
flavors:
	0 let P be satisfied any number of times with backtracking
	  (no control)
	1 let P be satisfied exactly once and report an error if it
	  can't be satisfied (e.g. many built-ins and "low-level"
	  user-defined procedures)
	2 let P be satisfied zero times (not P)
and very occasionally one needs
	3 let P be satisfied at most once
	4 see if P is satisfiable but don't bind any variables

There is a fairly simple and neat way of capturing all these options with the
following four operators:

       [ P	P has >=1 solutions -- normal case ]
	-P	P has  0  solutions (don't bind vars, no backtracking)
	+P	P has >=1 solutions (don't bind vars, no backtracking)

	@X	print error and fail unless X <one of the above>
	 X!	allow only 1 solution for X

These operators combine to give the following control options:

 -P   fail if there are any Ps (= not),		don't bind vars, succeed once
 +P   fail if there are no Ps,			don't bind vars, succeed once
@-P   print error & fail if there are any Ps,	don't bind vars, succeed once
@+P   print error & fail if there are no Ps,	don't bind vars, succeed once
 P    fail if there are no Ps,			do    bind vars, succeed N times
@P    print error & fail if there are no Ps,	do    bind vars, succeed N times
 P!   fail if there are no Ps,			do    bind vars, succeed once
@P!   print error & fail if there are no Ps,	do    bind vars, succeed once

So, with only four operators and at most two per term, we get the full range
of ways to control how a goal can be satisfied. These are almost enough to
replace the cut, but not quite. There are two types of control situations that
these operators can't handle. Once of them is
		pred :- !, fail.
and the other is when we exploit lexical clause ordering, as in
		call( (P,Q) ) :- !, call(P), call(Q).
		call( X ) :- ...    % now can ignore the (P,Q) possibility
In both these cases the cut controls not an individual goal, but affects
the interpretation of the entire set of related clauses (naughty naughty).

I'd like to hear why cut was designed as it was, given that yes, there is only
one cut and it's strong enough to do everything we want, but it's not very
readable and it's also not very intuitive to novices. Is it or is it not a
good idea to replace cut with operators like those I defined above, provided
that the other uses of cut can also be rationalized?

marcel@ads.com