🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.prolog
Article #1258

Re: Standards question: behavior of arg/3

#1258
From: Richard A. O'Keefe
Date: Wed, 17 Oct 1990 05:55
209 lines
10424 bytes
In article <9888@bunny.GTE.COM>, bs30@GTE.COM (Bernard Silver) writes:
> The ISO draft specifies that arg(N,Term,Arg) (with N and Term
> instantiated) reports a range error if N is greater than the arity of
> Term.   In contrast, many existing Prologs just quietly fail in
> this situation.  Which do you prefer?  In particular, do your programs
> ever rely on the quiet failure to end a recursion?

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.

What does that tell us about arg/3?  Well, could arg/3 be defined
by a table?  Yes:
	arg(1, ''(X), X).
	arg(1, ''(X,_), X).
	...
	arg(1, '\1'(X), X).
	arg(1, '\1'(X,_), X).
	...
and so on.  For each function symbol <F>, for each positive arity
<A>, and for each integer 1 <= <N> <= <A> there is a fact

	arg(<N>, <F>(X<1>,...,X<N>,...,X<A>), X<N>).

This is an infinite table.  For any query to arg/3, the table defines
a set of possible answers.  In cases where this set of answers is not
finite, it makes sense for the Prolog processor either to delay the
goal (as NU Prolog can) or to report an instantiation_fault (as
Quintus Prolog can).  When do we have this unbounded nondeterminism?
When the Term argument is a variable.  As long as the Term argument
is not a variable, the solution set is finite.  So the query

	?- arg(N, 27, X).

makes perfect sense and has no solutions, while the query

	?- arg(N, [a,b,c], X).

makes perfect sense and has two solutions

	N = 1, X = a ;
	N = 2, X = [b,c] .

THIS IS A CHANGE FROM DEC-10 PROLOG.  It is a change that shouldn't
break any sensible program, and is easy to repair:  a program which
relied on ?- arg(N, T, X) failing when var(N) can be repaired by
adding	nonvar(N) or integer(N) in front of it.

Remember that the model is "pure predicates should act exactly as if
defined by a possibly infinite table, except that a processor must
report an error if it is unable to simulate the table exactly, and
that it may report type failures."

What counts as a type?  Only things that have a standard recognition
predicate.  So integer and float and atom and so on count as 'types'
by virtue of the existence of the predicates integer/1, float/1,
atom/1, and so on.  A range like "1..13" does not count as a type.
Why allow type failures to be reported?  To help people find places
in their code where they have accidentally switched two arguments.
The first argument of arg/3 will almost always be an integer.
The second argument of arg/3 will almost always be a compound term.
Reporting a type failure if the first argument is not an integer
will help people who think of the "subscript" following the "array".
So a type failure report is sufficiently useful to over-ride the
normal preference for exact simulation of the table.

What, however, is the advantage of whining about values of N that
like outside the range?  Very little, I should think.  In particular,
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.)
If arg/3 is implemented sensibly, you have only to ask
	arg(3, Term, _)	% if this succeeds, arity{Term} >= 3

It is easy to see what kind of misconception about arg/3 could lead
to the first argument being a non-integer (argument order mistake).
I can't think of a likely misconception that would result in the
first argument being outside the range 1..arity{Term}, and I *can*
think of lots of reasons why that has been useful.

The most important thing, as far as I am concerned, is that as a 
programmer I should *NOT* have to remember a separate ad hoc set of
special whimsical rules about each built in predicate.  There should
be a *short* list of principles that I can memorize that cover the
*whole* collection of standard predicates.  So whatever is decided
about arg/3 should be made into a principle and *uniformly* applied
to all relevant predicates.

Here I have to say that the principle "Non calcitrandus in dentes artifex"
(the workman is not to be kicked in the teeth) has veto authority.  But
the principle I am recommending here is something *enabling*, it is a
requirement that built-in predicates should be as *general* as they can
be.  Most Prologs won't solve for the first argument of arg/3 (in
Quintus Prolog the predicate that does _that_ is called genarg/3).  But
allowing this doesn't break anything irreparably; I showed above how to
stop that if you want.


> Some Prologs ``extend'' arg/3 so that arg(0,Term,Arg) (attempts to)
> unifies Arg with the functor of Term.  (Quintus provides a library
> predicate to support this use).

This may be misleading.  The Quintus library provides a predicate
arg0/3 (and also a genarg0/3) *not* because anyone at Quintus ever
thought it was a sensible thing to do, but in order to help customers
who had written code for other Prologs which used such a kluge.  I never
intended it to be more than a piece of scaffolding that a customer could
discard once his code was corrected.

> Do people use this feature?  The ISO
> standard specifies that this use should raise an error, many existing
> Prologs either fail quietly or behave as above.

People manifestly *do* use this feature in Prologs that have it.
In part this is because of the incredible sloppiness of some Prolog
manuals, whose authors either never knew the difference between a
function symbol (an atom) and a functor (an atom/arity pair) or
didn't think their readers deserved precision.  There is also another
reason.  I think everyone who has written a theorem prover in Lisp
has realised at some time that you can treat the function symbol of
a term as if it were an argument of that term.  If you get that far,
you get a passing grade in Lisp hacking.  But in order to pass your
logic course, you have to take the next step, which is to realise that
the function symbol *isn't* an argument of the term.  Think about this
one fact:  you can use arg/3 to find out what any argument of a term
is OR to fill in an argument, but you can't "fill in" the function
symbol of a compound term because you had to supply the function
symbol and arity together in the first place in order to _have_ a
term.  Using arg/3 to get at the function symbol is unnecessary,
because functor/3 already exists, and it's inconsistent, because in
the model of what terms _are_ in ISO Prolog you can't "fill in" the
function symbol.

I repeat, the "non calcitrandus" principle has veto power over every
preference.  Where something is already portable, it should not be
broken.  But in this case, code that used arg(0, Term, X) to mean
what functor(Term, X, _) means was NOT portable, so making the standard
sensible won't make those programs any less portable than they were
before.

*IF* the ISO standard were to rule that arg/3 acts like the infinite
table we use to explain it, then it would be important to ensure
that every value of N accepted as input could be generated as output
and every value of N generated as output would be accepted as input,
and a program which calls
	?- arg(N, f(1,2), Z)
is entitled to be told by the standard how many solutions it is going
to get.

*HOWEVER* if the ISO standard were to rule that it is an error for N
to be uninstantiated, then it would be perfectly acceptable for the
standard to leave the behaviour of arg(N, T, A) *undefined* for
integral N outside the range 1..arity{T}.  In that case *both* Prolog
systems which are DEC-10 compatible *and* Prolog systems with the
pointless kluge would conform to the standard, vendors would not have
to change that part of their implementation, and the portablity of
code using the kluge would remain exactly what it is now.


> If you believe in quiet failure, when should arg/3 raise an error?

You shouldn't be asking questions about arg/3.  You should be asking
"what rule do you recommend for ALL built-in predicates?"  In this
case, *precisely* the same question about the domain of N applies to
the built-in predicate functor(Term, Symbol, Arity).  We should have
ONE answer.

> Is arg(-3,Term,Arg) ok?

Except for the omitted spaces (it should be arg(-3, Term, Arg)),
of course it is.

> What about arg(a,Term,Arg)?

Unlike arg(-3, Term, Arg), *this* one could arise from the misconception
that the argument order is arg(Term, Index, Arg), so it would make sense
to *allow* an implementation to report a type failure.  I would point
out that if you think of arg/3 as defined by that infinite table, the
query is sensible and false, so it also makes sense to *allow* an
implementation to deliver the defined result, namely 'false'.


I really can't stress enough that it is foolish to consider each
predicate separately.  As a programmer I shouldn't have to remember
half a dozen facts about errors PER BUILT-IN; I should just have half
a dozen facts about errors *for the whole standard* to remember.
This was obvious 6 years ago!  I mean, this is an *elementary*
principle of programming language design!

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.
Technical Details
Message-ID: <3992@goanna.cs.rmit.oz.au>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!samsung!munnari.oz.au!goanna!ok
References: <9888@bunny.GTE.COM>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!samsung!munnari.oz.au!goanna!ok
From: ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe)
Newsgroups: comp.lang.prolog
Subject: Re: Standards question: behavior of arg/3
Keywords: ANSI, standard
Message-ID: <3992@goanna.cs.rmit.oz.au>
Date: 17 Oct 90 05:55:31 GMT
References: <9888@bunny.GTE.COM>
Organization: Comp Sci, RMIT, Melbourne, Australia
Lines: 209