Article View: comp.lang.prolog
Article #1303Re: Standards question: behavior of arg/3
From: Micha Meier
Date: Mon, 22 Oct 1990 12:10
Date: Mon, 22 Oct 1990 12:10
189 lines
8843 bytes
8843 bytes
In article <3992@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
>
>I am sick of this kind of thing. Back in 1984 I proposed a set of
>"design principles", a *small* set of *very simple* rules, which
>would tell what the answer *had* to be. It is quite simply a BLUNDER
>to consider each predicate separately.
>
>The principle here was that something which could in principle be
>defined by a (possibly infinite) table should *act* just like that
>table. An error report would be *required* from a Prolog processor
>if it was unable to simulate the behaviour of the table exactly for
>some particular goal, and type errors would be permitted, but that's
>all.
I completely agree with Richard O'Keefe that there should be just a few
design principles and everything else should depend on them.
The problem is, however, how to select these principles to obtain a sensible
system. Since Prolog is based on SLD resolution and on first-order logic,
there seems to be no reason to introduce error messages or events
in case some arguments have a wrong type or when some predicates are missing,
because in this case the resolution step or unification just fails.
When we introduce built-in predicates to the language, the situation
changes, because not all of them can be defined as a set of axioms
of the language. (Let us leave out non-logical predicates, where
the situation is much clearer.) Those predicates, which can be defined
in Prolog, have then also a clear meaning and there seems no necessity
to introduce error messages.
One way to define some built-in predicates is as Richard O'Keefe
suggests, namely by means of a (possibly infinite) set of clauses.
Then, of course, if the set of possible answers is infinite,
the natural reaction is to issue an instantiation fault or
delay the execution, e.g. for call(Var) or functor(Var1, Var2, Var3).
However, when these logical principles are fully applied,
debugging programs becomes difficult.
In C-Prolog, the call of an undefined procedure simply fails,
and almost everyone agrees now that this is not always
the best thing to do, although it is really logical
and it follows straight from the resolution principle.
When you exchange the arguments of a built-in predicate and it then
simply fails, it is the same. When you write a loop and the bounds are
wrong, you may well appreciate somebody to tell you quickly, without a
debugger tete-a-tete.
Richard O'Keefe suggest that we apply the table method extensively,
but, exactly in case of arg/3, this would mean a different
semantics than in the vast majority of existing Prolog systems
(although not necessarily breaking most programs), so it is
in fact more like defining a new language, than to standardise
the old one.
Another example may be is/2:
5 is 3 + X
is completely logical and makes sense, however we cannot expect Prolog
to succeed here, even if the number of solutions is finite. The same
concerns the query
X is fred (from O'Keefe's Standard paper)
which would fail if is/2 were defined with a table, but this
failure would not mean anything reasonable to the programmer.
While O'Keefe supports type errors for bad types, Lee Nais says:
< ?- N = 1, arg(N, f(a), a), N = 2.
<
<With a left to right computation rule, we get the call arg(1, f(a), a),
<which everyone agrees should quietly succeed (then 1 = 2 fails).
<With a right to left computation rule (or if the goal was reordered
<somehow), we get the call arg(2, f(a), a).
And this would actually mean that no type errors are possible,
because in
?- N = 1, X is N + 1, N = fred.
executed in parallel, it is not clear with what arguments is/2
will be called first, and therefore it should fail when N equals to fred.
As you can see, it is really difficult to apply logical rules
to explaining what Prolog should do. O'Keefe further says
<I have frequently used
<
< p(N, T, ~stuff~) :-
< arg(N, T, A),
< !, % now we know that N >= 1
< ...
< p(0, T, ~stuff!) :-
< ...
<
<This seems to me to be a perfectly sensible use of arg/3. Further,
<suppose I want to test whether a term has 3 or more arguments. (It
<might be useful to do this in your own version of write/1, because
<that means that the function symbol won't be written as an operator.)
These are really bad arguments about the semantics of arg/3. If p/3
is called with an integer which is possibly zero, there is no
need to push the work to arg/3:
p(0, T, ~stuff!) :-
...
p(N, T, ~stuff~) :-
N > 0,
arg(N, T, A),
...
This will make the life easier for the compiler and it is also cleaner.
The compiler now knows that N must be a positive integer and so it
can unfold arg/3 in a shorter code, and no choice point is necessary.
Further O'Keefe says
<If arg/3 is implemented sensibly, you have only to ask
< arg(3, Term, _) % if this succeeds, arity{Term} >= 3
Why is it necessary to use arg/3 to test the arity of a functor
when we have functor/3?? The minimality principle has to be applied.
>> Is arg(-3,Term,Arg) ok?
>
>Except for the omitted spaces (it should be arg(-3, Term, Arg)),
>of course it is.
Again, from the logical point of view, it is, even the type is correct,
but it is hard to make me belive that the programs have to be written
this way. When such a call occurs, it is much more likely
that some loop did not have proper bound checks, and in this case
the programmer may expect an error message.
Dan Brahme says
<If the ISO standard arg(N, Term, Arg) reports error when N is greater
<than the arity, it is a nuisance.
<
<Then we cannot do
< bagof(Arg, N^arg(N, Term, Arg), Args).
<
<or
< bagof(Arg, N^(positive_integer(N), arg(N, Term, Arg)), Args).
< where positive_integer is
< positive_integer(1).
< positive_integer(N) :- positive_integer(N1), N is N1 + 1.
<
<to get a list of args.
Neither of these two are valid arguments. The first one will of course
have to report an instantiation fault, the second is the most
inefficient recoding of one half of Term =.. Args I can think of. If you insist
of coding =../2 yourself, do not use bagof or other failure driven
loops, use the normal recusrive definition:
arg_list(Term, List) :-
functor(Term, _, Arity),
arg_list(Term, Arity, [], List).
arg_list(_, 0, List, List).
arg_list(Term, I, SoFar, List) :-
I > 0, % so that the compiler generates no choice point
arg(I, Term, Arg),
I1 is I - 1,
arg_list(Term, I1, [Arg|SoFar], List).
This can be compiled into a very efficient iterative loop.
At ECRC, we were faced with the same problems when designing Sepia. Our solution
was in a way 'maximal', as opposed to the 'minimal' one of C-Prolog.
Sepia raises an event each time the predicate is not sufficiently
instantiated, an argument has a wrong type (where the type does not depend
on the value of other arguments, so functor(1.2, A, 1) fails)
or when the arguments that have to be the input ones
are not in the required domain.
In such a way, the built-in predicate succeeds whenever it is supposed
to, and whenever there might be something wrong, an event is raised.
The advantage of this approach is that everyone can redefine
the event handler to do what the user wants, e.g. to fail.
We have some compatibility packages for other Prolog dialects,
and it is interesting to note that for C-Prolog, the only
events to redefine to make all our C-Prolog programs work
was the behavior of arg/3 and undefined predicates.
When the coroutining mode is on, the user can e.g. redefine the
handler of the instantiation fault to delay the predicate,
the arithmetic delays by default.
We do not insist that the standard does what we do,
but it is a very interesting experience to work with such a system,
especially for people that work on a future Prolog standard.
So far nobody has complained that there are too many error
messages issued by Sepia, as a matter of fact, it imposes a better programming
style, as far as I can tell.
-- Micha Meier
--
USA micha%ecrc.de@pyramid.com MAIL Micha Meier
ECRC, Arabellastr. 17
EUROPE micha@ecrc.de 8000 Munich 81
West Germany
Technical Details
Message-ID:
<1753@ecrc.de>Path:
arc1-ca.pugleaf.net!utzoo!attcan!uunet!zaphod.mps.ohio-state.edu!sol.ctr.columbia.edu!ira.uka.de!fauern!tumuc!guug!ecrc!michaReferences:
<9888@bunny.GTE.COM> <3992@goanna.cs.rmit.oz.au>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!zaphod.mps.ohio-state.edu!sol.ctr.columbia.edu!ira.uka.de!fauern!tumuc!guug!ecrc!micha From: micha@ecrc.de (Micha Meier) Newsgroups: comp.lang.prolog Subject: Re: Standards question: behavior of arg/3 Keywords: ANSI, standard Message-ID: <1753@ecrc.de> Date: 22 Oct 90 12:10:58 GMT References: <9888@bunny.GTE.COM> <3992@goanna.cs.rmit.oz.au> Sender: news@ecrc.de Reply-To: micha@ecrc.de (Micha Meier) Organization: ECRC Lines: 189