[comp.lang.prolog] Standards question: behavior of arg/3

bs30@GTE.COM (Bernard Silver) (10/16/90)

The latest draft of the ISO Prolog standard has recently come out.
One thing that caught my attention is the specified error behaviour 
for the predicate arg/3.  We (the ANSI group) are interested in
finding out how people use this predicate.

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?

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).  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.

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

Is arg(-3,Term,Arg) ok?  What about arg(a,Term,Arg)?

We would appreciate any comments!

Newsgroups: comp.lang.prolog
Subject: Standards question: behavior of arg/3
Summary: 
Expires: 
Sender: bsilver@gte.com (Bernard Silver)  
Followup-To: 
Distribution: 
Organization: GTE Laboratories, Waltham MA
Keywords: ANSI, standard

The latest draft of the ISO Prolog standard has recently come out.
One thing that caught my attention is the specified error behaviour 
for the predicate arg/3.  We (the ANSI group) are interested in
finding out how people use this predicate.

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?

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).  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.

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

Is arg(-3,Term,Arg) ok?  What about arg(a,Term,Arg)?

We would appreciate any comments!

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/17/90)

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.

lee@munnari.oz.au (Lee Naish) (10/18/90)

Sorry if anyone gets this twice - my first attempted followup didn't seem
to work.

In article <3992@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
><Lots of sensible stuff which I agree with completely...>

I just thought I would expand on one point.

Treating predicates as (possibly infinite) tables is the "logical" thing
to do.  The more complicated the computation rule is, the more important
logical behaviour is.  Consider the following goal (which may occur
inside a perfectly reasonable computation):

	?- 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).
With a parallel computation rule we could get either of the above, or
not call arg/3 at all, or get the call arg(N, f(a), a).

One of the nice things about logic is that the order doesn't matter.
The goal above will run in any order in sequential or parallel NU-Prolog 
and the result will be the same - simple failure.

If you start putting in error messages you lose this (its even worse if
things like arg(N, T, A) fail silently, because the system does not respect
the logic and does not even tell you).

What about finding bugs?  Ideally, there should be a special debugging
mode where various warning messages are given.  Otherwise, the compromise
Richard suggested (allowing, but not enforcing, simple type checks)
seems reasonable.

In NU-Prolog, calls such as ``1 =< a'' results in an error message, then
failure.  I have generally found this useful (it normally indicates an
error), but on one occasion it was a nuisance.  Inside a type checker
which coroutines between bits of user code and type constraints, such
calls are quite acceptable and ideally should quietly fail.  I was forced
to write a separate predicate which explicitly checked the type before
calling the arithmetic test:

        % wait until term is nonvar then check its
        % an integer in the right range
?- check_range(A, _, _) when A.
check_range(A, L, U) :-
        integer(A),
        L =< A,
        A =< U.

Note that I had to force check_range to delay until A was instantiated,
otherwise I could not be sure that the integer check would be called
before =<.  Since this kind of coding is quite rare, =< producing error
messages is an acceptable compromise.

In summary, don't underestimate the nuisance value of error messages
being printed when the system is capable of behaving logically.
Standards should definitely not enforce such messages.  If possible they
should be relegated completely to a debugging mode.

	lee

brock@tuvie (Inst.f.Prakt.Info 1802) (10/18/90)

In article <3992@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
[...discussion of design principles for BIPs...]
>	?- arg(N, [a,b,c], X).
>makes perfect sense and has two solutions
>	N = 1, X = a ;
>	N = 2, X = [b,c] .

Which Prolog systems have arg/3 defined in this way?


Ulrich Neumerkel
ulrich@vip.at ulrich@vip.UUCP

brahme@vlsic2.ti.com (Dan Brahme) (10/18/90)

bs30@GTE.COM (Bernard Silver) writes:

>The latest draft of the ISO Prolog standard has recently come out.
>One thing that caught my attention is the specified error behaviour 
>for the predicate arg/3.  We (the ANSI group) are interested in
>finding out how people use this predicate.

First,
Can the ISO people post a readable draft of the standard that they 
are proposing to this news group?

>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?

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.


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

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

      arg(-3, Term, Arg) is OK
while arg(a, Term, Arg) is NOT OK and should report as a error.

>We would appreciate any comments!

I agree with Richard that you should have a set of
principles to decided when a predicate should or should
not report a error.
The other alternative is post all the built-in predicates
and a list of questions like the one for arg and let
everybody vote depending on what they feel should
be the behavior of the built-in. With this mechanism
the standard behavior of each predicate is determined
by majority vote. 

I think both approaches are fine by me. Though the second
approach fits in more with american style of democracy. :-)

Dan Brahme
Texas Instruments

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/19/90)

In article <1920@tuvie>, brock@tuvie (Inst.f.Prakt.Info 1802) writes:
> In article <3992@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
> [...discussion of design principles for evaluable predicates...]
> >	?- arg(N, [a,b,c], X).
> >makes perfect sense and has two solutions
> >	N = 1, X = a ;
> >	N = 2, X = [b,c] .

> Which Prolog systems have arg/3 defined in this way?

Re-read that carefully.  I said that the query makes sense and it has
two solutions.  I didn't say anything about implementations.  I _have_
seen a Prolog system which did this, but can no longer recall which.
Quintus Prolog has an operation that does this, it just calls it
genarg/3 rather than arg/3.

The thing which would make extending arg/3 tricky on the WAM is that
WAM registers don't survive choice point creation, so that open-coding
non-determinate evaluable predicates doesn't help a lot.  If a compiler
can do data-flow analysis, it can pick the determinate case a lot of
the time, and I'm not even talking about inter-clause analysis.
Consider

	first_N_args_the_same(N, T1, T2) :-
		integer(N),
		first_N_args_the_same(0, T1, T2, N).

	first_N_args_the_same(I, T1, T2, N) :-
		(   I < N ->
		    J is I+1,
		    arg(J, T1, X),	% number(J) is *known* here
		    arg(J, T2, X),	% and here, so open-coding pays off
		    first_N_args_the_same(J, T1, T2, N)
		;   I =:= N
		).

As for the benefit of making arg/3 logical, consider

	path_arg([], Term, Term).
	path_arg([Selector|Selectors], Term, SubTerm) :-
		arg(Selector, Term, Arg),
		path_arg(Selectors, Arg, SubTerm).

If arg/3 can solve for its first argument, then so can path_arg/3.

The wording in my 1984 document (BSI number PS/6) was very carefully
chosen to insist that when the logic says there is a unique answer,
an implementation must find it, but when there are many answers, an
implementation may give up, provided it admits to the fault.  For
example, a query like

	times(X, Y, 1000000000000000000000000000)

has a well defined finite solution set, but an implementation may not
be prepared to go to the trouble of finding it.  (The next revision
of that document defined times/3 to work on rationals, not integers,
so the solution set is infinite.  This is just an example.)

(For the record, when you ask
	?- arg(N, [a,b,c], X).
SICStus Prolog 0.7 fails quietly, and NU Prolog reports that the
query ``floundered''.)

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

pgl@cup.portal.com (Peter G Ludemann) (10/19/90)

| In article <3992@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au 
| (Richard A. O'Keefe) writes:
| [...discussion of design principles for BIPs...]
| >	?- arg(N, [a,b,c], X).
| >makes perfect sense and has two solutions
| >	N = 1, X = a ;
| >	N = 2, X = [b,c] .
| 
| Which Prolog systems have arg/3 defined in this way?

I implemented such an arg/3 at university -- it never even
occured to me to check how Edinburgh does it because it seemed
the natural way to do things.  And IBM-Prolog also provides
such a facility (not implemented nor designed by me, by the
way) but it also provides the conventional Edinburgh flavour
for those who demand compatibility.  [Richard O'Keefe notes
that his proposal is not compatible with Edinburgh Prolog,
by the way -- "a foolish consistency is the hobgoblin of small
minds" (Emerson, I think).]

Richard might not like the fact that IBM's arg/3 allows
arg(0,f(a),f) -- that's easily fixed by using a delayed inequality:
    wait:(N ==/ 0) & arg(N,F,X)

IBM also a delayed version of arg/3 which will wait until
the functor is instantiated.  It allows things like:
    :- wait:arg(N, F, X) , F = f(a) , N = 1.
to succeed with N=1, F=f(a), X=a.

Here's an annotated console trace:

  % --- switch to Edinburgh syntax and predicates:

  <- switch(synt2).
   goal : <- switch(synt2) .
   48ms success
  <- switch(synt2) .

  % --- demonstrate compatibility with Edinburgh predicate arg/3:

  :- arg(0, [a,b,c], N) .
   goal : <- arg(0,[a,b,c],V1) .
   0ms fail

  <- arg(N, [a,b,c], X) .  % no error message!
   goal : <- arg(V1,[a,b,c],V2) .
   0ms fail

  :- arg(1, [a,b,c], X) .  % succeeds with X = a
   goal : :- arg(1,[a,b,c],V1) .
   0ms success
  :- arg(1,[a,b,c],a) .

  % --- now show what happens with the non-Edinburgh flavour
  % --- of arg/3.  We must use the "built" prefix to get it
  % --- because the Edinburgh flavour (built2:arg/3) is default
  % --- when we're in Edinburgh syntax.  Alternatively, we could
  % --- have declared "built" to be the default prefix for arg/3.

  :- built:arg(N, [a,b,c], X) , built:write(N:X) , fail .
   goal : :- built : arg(V1,[a,b,c],V2) , built : write(V1 : V2) , fail .
  0 : '.' .
  1 : a .
  2 : [b,c] .
   0ms fail

- Peter Ludemann    ludemann@mlpvm1.iinus1.ibm.com

lee@munnari.oz.au (Lee Naish) (10/19/90)

> >	?- arg(N, [a,b,c], X).
> >makes perfect sense and has two solutions
> >	N = 1, X = a ;
> >	N = 2, X = [b,c] .
>
> Which Prolog systems have arg/3 defined in this way?

MU-Prolog 3.2db
1?- arg(N, [a,b,c], X).
 
N = 1, 
X = a  ;

N = 2,
X = [b, c]  ;

fail.
2?- 

As Richard pointed out, NU-Prolog delays this goal.  NU-Prolog is a WAM
based system and open codes arg/3 - it knows it is deterministic.
MU-Prolog is an interpreter (the implementation of arg/3 is
particularly inefficient - I put in a quick hack and never got around to
doing it properly, but MU-Prolog isn't a really "serious" Prolog system
anyway).

Richard also mentioned Sicstus failing with this goal.  I think its
unfortunate that Sicstus does not make more of its coroutining
facilities.  There lots of builtins which could be made to behave more
logically using these facilities.  One possible reason for not doing
this is to remain compatible with various other Prolog systems which
fail silently.  There is something to be said for this.  I have had the
"pleasure" of porting a 30,000 line program written in (an old version
of) Quintus Prolog to NU-Prolog.  One of the nastiest bugs was a call to
functor/3 which failed in Quintus because it was insuffiiently
instantiated, but delayed in NU-Prolog.  I'm not sure what the latest
version of Quintus does.  I hope more systems start producing error
messages in situations like this (you can always add a call to nonvar/1
if you really want failure).

Another way of solving the porting problem is to support two versions of
functor, arg, is, <, etc, etc, as IBM Prolog does.  You should
definitely do this for some builtins, such as the type tests (integer
etc).  However, I think Prolog vendors would be doing every?one a favour
if they made functor etc produce error messages when insufficiently
instantiated.  I don't think it would hurt to put this into the
standard(s) either, even though most existing systems would not
conform.  I haven't seen any recent draft standards - do they discuss the
general class of errors called instantiation faults?  Richard's 1984 draft
did.

	lee

lee@munnari.oz.au (Lee Naish) (10/19/90)

In article <brahme.656264677@vlsic2> brahme@vlsic2.ti.com (Dan Brahme) writes:
>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).

Sorry to be pedantic, but you can't do that anyway in most systems (see
other postings on this topic).

>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.

This code loops.

One way to do it properly is to use functor/3 to get the arity, then use
this as a bound on the integers you generate (use between/3 in Quintus
or iota in NU-Prolog).

However, if you really want to get a list of all the args in a term you
do not want to use bagof.  There is already a builtin for that:

	Term =.. [Functor | Args]

	lee

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/22/90)

In article <5816@munnari.oz.au>, lee@munnari.oz.au (Lee Naish) writes:
> I have had the
> "pleasure" of porting a 30,000 line program written in (an old version
> of) Quintus Prolog to NU-Prolog.  One of the nastiest bugs was a call to
> functor/3 which failed in Quintus because it was insuffiiently
> instantiated, but delayed in NU-Prolog.  I'm not sure what the latest
> version of Quintus does.

It reports the instantiation fault.  Silent failure when the real problem
is not that there are _no_ solutions but that there are infinitely many
is clearly (a) wrong and (b) unhelpful.

> I hope more systems start producing error
> messages in situations like this.

QP3.0 is really thorough about this, and to be fair, IF/Prolog always
did produce error messages.  (To be even fairer, IF/Prolog also produced
error messages in several cases where the query made sense and had a
definite answer...)

> I haven't seen any recent draft standards - do they discuss the
> general class of errors called instantiation faults?

Yes, EPs in the ISO draft do report instantiation faults.
It's not described as a general rule; you have to check each separate
predicate description.

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/22/90)

In article <35019@cup.portal.com>, pgl@cup.portal.com (Peter G Ludemann) writes:
> Richard might not like the fact that IBM's arg/3 allows
> arg(0,f(a),f) -- that's easily fixed by using a delayed inequality:
>     wait:(N ==/ 0) & arg(N,F,X)

Ah, I see:  if N is already instantiated the N==/0 (what the dickens
does ==/ mean?) goal will fail, if N is not instantiated arg/3 will
generate the *bogus* incredibly meaningless N=0 solution which will
then be rejected.  Good one.

If someone really wants arg0/3, by all means let them have it, but
let it be called arg0/3, not arg/3.

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

micha@ecrc.de (Micha Meier) (10/22/90)

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

pgl@cup.portal.com (Peter G Ludemann) (10/23/90)

| > Richard might not like the fact that IBM's arg/3 allows
| > arg(0,f(a),f) -- that's easily fixed by using a delayed inequality:
| >     wait:(N ==/ 0) & arg(N,F,X)
| 
| Ah, I see:  if N is already instantiated the N==/0 (what the dickens
| does ==/ mean?) goal will fail, ...

'==/' is IBM-Prolog-ese for not equal, treating varaibles as distinct.
I put the ==/ inside wait:(...) so that the test will delay until
everything is instantiated (by the way, Richard, this is all in
the manual which I sent you).  Of course, I could have been less
fancy and simply done:
	arg(N,F,X) & N =/ 0
('=/' is the usual inequality.)  It all depends whether you think that
N will usually be instantiated or not, so pick one goal ordering or
or the other, to get the most efficient execution.

The "priority prefix" technique of designating which predicate
should be used is a very powerful technique.  For example, suppose
that I wanted arg/3 to generate an error whenever the N was not
ground to an integer.  I could program this:
	my:arg(N,F,X) :- is_not_ground_int(N), !, error(...) .
	my:arg(N,F,X) :- built2:arg(N,F,X) .
and declare that 'my' is the prefix to use whenever arg/3 is
encountered (by default, 'built' or 'built2' would be used).  
In this way, I can trivially get the semantics I want without
having to read through 30,000 lines of source.

Richard and I had a long discussion about arg/3 vs arg0/3.  I think
that I can summarize it as follows:

	- Edinburgh treats functor names as special and doesn't allow
	  input like "f()".  For Edinburgh compatibility, arg(0,...)
	  should fail.

	- Prolog-II, Prolog-III, IBM-Prolog (and others?) have a more
	  generalized view of functors.  For example, using this view,
	  one could define =../2 by the infinite list of clauses: 
		F      =.. [F] . 
		F()    =.. [F] .
		F(A)   =.. [F,A] .
		F(A,B) =.. [F,A,B] .
			etc.
	  Furthermore, a principal functor is not limited to be an atom;
	  it can be any term.  Using the syntax <<f(a),2,3>>, the principal
	  functor is itself a functor: f(a).
	
	  Of course, this extended view is *not* Edinburgh Prolog
	  (which also doesn't properly support the "infinite rational
	  trees" of Prolog-II and IBM-Prolog); and there is no particular
	  reason for ISO to support this extension to functors -- and every
	  Prolog which claims to be Edinburgh compatible should support the
	  original Edinburgh design (whether or not ISO should support the
	  original Edinburgh design is an entirely different issue).

- peter ludemann	ludemann@mlpvm1.iinus1.ibm.com

		(the above are my opinions and almost certainly not
		 those of my employer nor of my co-workers)

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/23/90)

In article <35150@cup.portal.com>, pgl@cup.portal.com (Peter G Ludemann) writes:
> '==/' is IBM-Prolog-ese for not equal, treating varaibles as distinct.
> I put the ==/ inside wait:(...) so that the test will delay until
> everything is instantiated (by the way, Richard, this is all in
> the manual which I sent you).

Apology.  I knew it was in the manual.  My point was that the name was
rather opaque.  Given that \ is in the EBCDIC character set (and in fact
IBM Prolog supports the Edinburgh name \==) I don't see what the point
of replacing one opaque but at least familiar symbol by an equally
opaque but unfamiliar symbol was.  That was the point.  

> 	- Edinburgh treats functor names as special and doesn't allow
> 	  input like "f()".  For Edinburgh compatibility, arg(0,...)
> 	  should fail.

There are two distinct things here.
1.  Edinburgh Prolog data structures are copied from the usual
    presentation of terms in first-order logic.  Having the functor
    (e.g. f/3) of a term (e.g. f(a,b,c)) be part of the structure of
    the term is not the "Edinburgh" way, it's the usual way of
    describing terms in first-order logic.  PopLog makes the arity
    part of the structure, but not the function symbol, so it would
    represent f(a,b,c) by the vector #(a b c f).

2.  Edinburgh syntax does not allow f() as input.
    Changing the public-domain parser so that it WAS legal would
    require adding about two lines [wait a bit...  change the clause

    read_atom(Functor, ['('|S1], Precedence, Answer, S) :- !,
	parse(S1, 999, Arg1, S2),		% look for "," or ")"
	read_args(S2, RestArgs, S3),
	Term =.. [Functor,Arg1|RestArgs],
	exprtl0(S3, Term, Precedence, Answer, S).

    to

    read_atom(Functor, ['('|S1], Precedence, Answer, S) :- !,
	read_args_1(S1, Args, S2),
	Term =.. [Functor|Args],
	exprtl0(S2, Term, Precedence, Answer, S).

    read_args_1([')'|S1], [], S1) :- !.		% empty argument list ()
    read_args_1(S1, [Arg|Args], S3) :-		% non-empty argument list
	parse(S1, 999, Arg, S2),
	read_args(S2, Args, S3).

    ... Done!]
    This would have the effect of making f() == f, as it should be.

> 	- Prolog-II, Prolog-III, IBM-Prolog (and others?) have a more
> 	  generalized view of functors.  For example, using this view,
> 	  one could define =../2 by the infinite list of clauses: 
> 		F      =.. [F] . 	<******* NB
> 		F()    =.. [F] .	<******* NB
> 		F(A)   =.. [F,A] .
> 		F(A,B) =.. [F,A,B] .
> 			etc.

I haven't been able to try this in IBM Prolog, but in one Prolog that
I _have_ got that takes this kind of thing (the arity is part of the
structure but the function symbol is not) there were two unpleasant
effects:  first, the wretched thing didn't take the function symbol
into account when indexing (I am *not* saying that IBM Prolog doesn't,
not at all, but if you can't rely on the function symbol being _there_
it's a wee bit tricky to index on it), and second precisely this
anomaly occurred:
	f =.. X 	-> X = [f]
	f() =.. X	-> X = [f]
	T =.. [f]	-> T = f
but NOT	T =.. [f]	-> T = f()

Why do I have to keep on saying that we want our EPs to satisfy
SIMPLE laws?  Here are two laws which univ satisfies in Prologs whose
data structures are based on the usual description of terms in
first-order logic:

	if at any instant T1 =.. L1, T2 =.. L2, L1 == L2 would succeed,
	then at that instant T1 = T2 would succeed.
	"univ is an isomorphism between terms and certain lists"

	immediately after doing T1 =.. L1,
	T2 =.. L1, T2 == T1 would succeed (where T2 is a new variable) and
	T1 =.. L2, L2 == L1 would succeed (where L2 is a new variable)

The table exhibited by Ludemann does not obey these laws,
and I honestly cannot see that as an improvement.

> 	  Furthermore, a principal functor is not limited to be an atom;
> 	  it can be any term.  Using the syntax <<f(a),2,3>>, the principal
> 	  functor is itself a functor: f(a).

The functor of no term in an Edinburgh Prolog is an atom.
Functors are Atom/Arity PAIRS.  A function SYMBOL can be an atom
(or, for arity 0, a number).

Just how much of a generalisation of Edinburgh data structures is it
to allow non-atomic function symbols?  None:  it's a special case.
To get these "generalised" structures, all you have to do is confine
yourself to one function symbol, say '', and then write
	''(''(f,a),2,3)
Big deal.

Given that anything which can be done using ``improper'' terms
can be done using perfectly ordinary terms, and what is more can
be done with the same time and space bounds, and given that ``improper''
terms just never come up in the design method that I follow, and
given that ``improper'' terms do *not* correctly mimic higher-order
logic, I don't quite see the point of them.  From my own experience,
this is the kind of ``hack'' that an undergraduate feels ever so
proud of having discovered, and then realises a few years later isn't
such a good idea after all.  It just doesn't buy you anything you
can't get without it.

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/23/90)

In article <1753@ecrc.de>, micha@ecrc.de (Micha Meier) writes:
> 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.

I didn't find it all that hard.  What did I overlook?

> 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.)

Eh?  EPs fall into four categories:
    -- commands (change the state of the world)
    -- state functions (don't change anything themselves but
	are affected by commands)
    -- pseudo-predicates (don't depend on the state of the
	world, do depend on the instantiation state of their
	arguments, hence on evaluation order)
    -- pure predicates (don't depend on the state of the world)
Pseudo-predicates can't be defined by (possibly infinite) sets
of facts, but state functions and pure predicates can.  The
pseudo-predicates can, to the best of my knowledge, be defined
in terms of var/1, nonvar/1, and possibly infinite sets of facts.

> Those predicates, which can be defined
> in Prolog, have then also a clear meaning and there seems no necessity
> to introduce error messages.

Let's keep things clear.  Let's _not_ talk about error MESSAGES.
Those are reports intended for human consumption.  Let's talk about
error detection and signalling.

The design I did for Quintus last year split errors into four
main groups:
	disasters		Things went so badly wrong that
				the error handling system can't
				be entered.
	faults			A query appears to make sense, but
				the Prolog system is unable to
				process it (not enough memory,
				numeric over/underflow, unsupported
				data type, file server dropped dead,
				and so on)
	errors			a command could not be obeyed because
				an object identifier is malformed or
				fails to identify an object or the
				operation is not permitted, &c
				or a state function asking about some
				object couldn't be evaluated for a
				similar reason
	failures		a query makes sense, and logically has
				the answer 'no', but it's most likely
				and error.

(That's why I keep saying "instantiation FAULT"; what one Prolog will
signal as an exception another may be able to handle by suspending.)
Disasters and faults can happen at any time.  Errors can only happen
when it would be INCORRECT to fail.  (For example, if I ask
	clause(true, Head, Body)
either I should get a clause back, or I should get an error signalled
because I have no permission to look at the clauses of true/0.  It
would be WRONG to fail here.)  "failure" signals could well be optional;
they are in effect run-type type checks.

Disasters, faults, and errors have a very simple criterion:  if the
Prolog system can determine the correct answer (including failure as
a correct answer) it should do so, and when and only when it is not
able to determine the correct answer, that is either a disaster, a
fault, or an error, depending on the reason why the correct answer
can't be obtained.

> However, when these logical principles are fully applied,
> debugging programs becomes difficult.

Oh?  How?  Exception detection and signalling is NOT repeat *NOT*
a debugging feature.  Exception handling is all about not producing
incorrect answers.  Debugging goes further.  You might well call
'failure' messages, e.g.
 
	! Type failure in argument 1 of arg/3
	! integer expected, but f(a,b,c) found
	! Goal: arg(f(a,b,c), 1, _235)

a debugging tool.  I wouldn't quarrel with that.  But it's not the
only debugging tool.  A programmer should be able to attach a
"critic" to any predicate that will check lots of things which
aren't technically errors.

> 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.

Strictly speaking, it _would_ be different DEC-10 Prolog (but not
from MU Prolog or IBM Prolog).  But then so is producing error
reports different!  There is no real likelihood of the ISO version
of arg/3 being eactly like DEC-10 Prolog's arg/3.  A change which
provides more CORRECT answers surely is no bad thing?  We're not
talking about taking _working_ programs and breaking them, we're
talking about programs which didn't work properly and either
reporting this fact or making them work properly.

> in fact more like defining a new language, than to standardise
> the old one.

Not at all.  There is prior art.  We _can't_ be compatible with
_every_ previous Prolog.  There _are_ rough spots that have to
be spelled out, and precisely because these were grey areas in
the past we're going to differ from previous systems.  It's important
to minimise the differences.  Extremely important.  But extending the
_specification_ of an EP to say that it works in cases which previously
were not well defined, that's not much like defining a new language.

> 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.

No, the number of solutions here is not finite.  Plug into X any ground
arithmetic formula which evaluates to 2 and you have a solution:
	X = 2; X = 1+1; X = 0+2; X = -1+3; X = -2+4; ...
The EP plus/3, however, *should* be solved in such a case:
	plus(3, X, 5)
does have a unique solution for X, namely X = 5.  (plus/3 only makes sense
for rational numbers, not approximate numbers such as floats.)

> 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.

It's what I called a "type failure".  It does mean something sensible:
"'fred' is not an arithmetic expression."  In QP you would get a
message something like
	! Type failure in argument 2 of is/2
	! Arithmetic expression expected but fred found
	! Goal:  _234 is fred
If you want to switch this off, fine.  There are precisely three reasons
why a call
	Lhs is Rhs
may fail:
	- Rhs is not a well-formed arithmetic expression
	- Rhs asks for a value which definitely does not exist
	  (such as 1 // 0 or sqrt(-1.0))
	- Lhs is already instantiated to something which doesn't
	  unify with the value of Rhs
If Lhs is a new variable, then only the other possibilities exist.

> O'Keefe further says
> <I have frequently used
> <                arg(N, T, A),
> <                !,              % now we know that N >= 1
> 
> 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:

Hello Mr Thought Policeman!  Yes, I *can* rewrite my code.  Why
*should* I?  Why "push the work to" _me_?  Why is it ok to break
code that relied on a defined property of arg/3?  Especially when
you consider that I wrote PS/6 and sent it to the net back in 1984
precisely to make clear what it was that I _wanted_ to be able to
rely on?  The kind of argument here will apply to every kind of
type and range checking.  For example, concerning division by 0,
"there is no need to push the work to _//_", we could allow 
	X is Y//Z
to return any random junk you like when Z = 0, the programmer *could*
have written the code as
	(   Z =:= 0 -> signal_error(/* an appropriate error term */)
	;/* Z =\= 0 */ X is Y//Z
	).

> <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.

Who said it was *necessary*?  All I said was that it was *possible*;
if I want to know whether there is a 3rd argument I can ask it directly
without asking what the arity actually is.  (As it happens I don't do
that myself.)

WHAT minimality principle?  The BSI and ISO mobs have made much
mention of a minimality principle, but they have never said what it
IS.  Let's see:  if there is one way of doing something, there shouldn't
be another?  Right:  then let us have (<)/2 alone, and not have
(>=)/2, (>)/2, or (=<)/2; let us leave out (=)/2 because the user can
define it, let us leave out (=..)/2 because the user can define it,
let us leave out read/1 because the user can define it, let's leave
out binary _-_, because we can always do -Y+X instead of X-Y, ...
This is ridiculous.

Let me be completely frank here:  not only have I been unable to discover
what the ISO mob mean by "minimality", the vague ideas I have about what
it might mean seem for the most part to be very bad ideas.

> 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.

It depends on which version of C Prolog you had.  The 1.*.edai
versions were a wee bit more thorough about reporting errors.

> We do not insist that the standard does what we do,
> but it is a very interesting experience to work with such a system,

It sounds rather like QP.

[I hope someone on the ANSI or ISO committees is keeping a copy of
this stuff.]
-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

pereira@alice.att.com (Fernando Pereira) (10/23/90)

In article <35150@cup.portal.com> pgl@cup.portal.com (Peter G Ludemann) writes:
>	- Prolog-II, Prolog-III, IBM-Prolog (and others?) have a more
>	  generalized view of functors.  For example, using this view,
>	  one could define =../2 by the infinite list of clauses: 
>		F      =.. [F] . 
>		F()    =.. [F] .
>		F(A)   =.. [F,A] .
>		F(A,B) =.. [F,A,B] .
>			etc.
>	  Furthermore, a principal functor is not limited to be an atom;
>	  it can be any term.  Using the syntax <<f(a),2,3>>, the principal
>	  functor is itself a functor: f(a).
>	
>	  Of course, this extended view is *not* Edinburgh Prolog
>	  (which also doesn't properly support the "infinite rational
>	  trees" of Prolog-II and IBM-Prolog)

More seriously, it is also incompatible with standard logical and
algebraic views of the universe of terms given as a free algebra
over a signature given by the set of function symbols (with their arities).
This is not just formal pedantry. Function symbols allow us to make more
distinctions than are possible in a system based on a single `cons'
constructor. Of course, it is possible to *encode* the terms over any
finite signature as terms over a signature including just cons/2 and
a suitable set of constants (to fake the function symbols), but such
encodings are error-prone. There's preciously little data abstraction
in the standard term algebra as it is. Obliterating the distinctions
afforded by function symbols doesn't make the situation any better.
In contrast, support for rational terms does not eliminate existing
distinctions.

Fernando Pereira
2D-447, AT&T Bell Laboratories
600 Mountain Ave, Murray Hill, NJ 07974
pereira@research.att.com

pereira@alice.att.com (Fernando Pereira) (10/23/90)

In article <1753@ecrc.de> micha@ecrc.de (Micha Meier) writes:
>[...]
>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.

The deeper issue that is lurking here is that of types in logic programs.
Straight FOL has nothing to say about types. In programming based on
the lambda calculus, two main typing paradigms have been used:

	1. The underlying semantics is untyped, and type judgments are
	compile-time provable constraints on the domains and ranges of
	functions (for example, the identity function \x.x maps any
	set of values to itself, therefore it has type /\A.A->A). This
	is the approach used by Milner for the ML type system

	2. The underlying semantics is typed, and type judgments are
	well-formedness judgments on expressions given the meanings
	of the constants in them. This is the usual view of typed
	lambda calculi in logic.

Following the first view, the typing + : int * int -> int just states that
when given two ints + delivers an int. To account for typing failures one
needs to add to this that + will produce a special distinguished "bad"
valund 'wrong' when given anything else. Then typing failure corresponds to
not being able to show that an expression never yields 'wrong'. We could
adopt a version of this for Prolog. Any builtin would be seen as a
convenient proxy for some relation, *except* that Prolog does not know
how to compute the relation for arguments outside the intended domain.
That is, it is not that

	(foo, bar(a), a)

is not in the relation arg/3, but rather that Prolog has no way of knowing
whether it should be or not, and thus it issues a domain fault. This
is analogous to the instantiation fault on

	?- arg(X, foo(a), Y)

where Prolog (assuming no delaying) has no way of determining the
truth value of the query. Thus, my addition to Richard's design principles
is:

	Each builtin comes with an intended domain of applicability.
	Prolog has no way of knowing whether the builtin holds of
	argument values outside that domain, and will therefore
	issue a domain fault.

Fernando Pereira
2D-447, AT&T Bell Laboratories
600 Mountain Ave, Murray Hill, NJ 07974
pereira@research.att.com

bs30@sirius.gte.com (Bernard Silver) (10/24/90)

In article <4055@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:

   [I hope someone on the ANSI or ISO committees is keeping a copy of
   this stuff.]

Yes, I am!  I also received a lot of email on the original question of
arg/3.  As you might expect, there are several different points of view:
some support the infinite table approach, others view 
arg(N, Term, Arg)  ("extra" spaces for Richard) with N greater than the
arity of Term as a grave error, similar to exceeding the bounds of an array,
and thus must be reported.

To answer Richard's original ``attack'' on my posting:  I agree that
it is not a good idea to have a separate convention for each predicate.
However, that is the way the standard has evolved (and I don't accept
the blame for this as I have only joined recently!)  I feel that very few
people involved  would agree to redo the predicates in accordance with
your (or someone else's) design methodology.  So, one viable possibility is
to try to replace the particular definitions that we do not like.

We decided to post the arg/3 question to get feedback to that specific issue,
and also to see if people were interested in commenting on standards
in general.   In the past, Richard has been accused the standards bodies
of not caring about existing practice, suggesting that they are isolated
from the real user community.  This was a small attempt to address such
concerns.  

I got lots of feedback on arg/3 and related issues, so if the rest of 
ANSI agrees, we may post other questions in future, hopefully in a 
more structured context.

Richard, if you can send me two free plane tickets to IJCAI, I'll be willing
to forget your attack (and I'll put spaces after commas).


--
				Bernard Silver
				GTE Laboratories
				bsilver@gte.com
				(617) 466-2663

bs30@sirius.gte.com (Bernard Silver) (10/24/90)

I forgot to say:  Thank you to everyone who replied to the original
post, whether by email or by posting.  Your feedback is appreciated.
--
				Bernard Silver
				GTE Laboratories
				bsilver@gte.com
				(617) 466-2663

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/24/90)

In article <BS30.90Oct23152733@sirius.gte.com>, bs30@sirius.gte.com (Bernard Silver) writes:
> Richard, if you can send me two free plane tickets to IJCAI, I'll be willing
> to forget your attack (and I'll put spaces after commas).

I don't understand this.  I'm not going to IJCAI myself.  I'm not even
going to be able to go to NACLP-90 (something to do with a shortage of
money).  Why should I give someone else plane tickets?

Putting spaces after the commas that separate arguments of predicates
(but not of data values) makes code *much* more readable.  Commas are
used for enough things in Prolog that taking professional care with
them can be a big help.

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

micha@ecrc.de (Micha Meier) (10/25/90)

In article <3992@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
>> Is arg(-3,Term,Arg) ok?
>
>Except for the omitted spaces (it should be arg(-3, Term, Arg)),
>of course it is.

This actually reminds me - did Quintus change their way of printing Prolog
terms in the new release, or is everything still printed without spaces?
What is the position of ISO in this matter? I find it should be
standardised. It makes comparison of the output from two Prolog systems
easier.


--Micha
--
E-MAIL  micha@ecrc.de            	MAIL	Micha Meier
						ECRC, Arabellastr. 17
                     				8000 Munich 81
						Germany

cleary@cs-sun-fsc.cpsc.ucalgary.ca (John Cleary) (10/29/90)

I very glad to see some real discussion of errors in Prolog.
This topic is important and I have not yet seen any principled and
acceptable solution.

However, it is worth noting that the question is not as simple as it seems.
Consider for example is.

Now
	X is fred
should clearly generate an error message :-).  But at least once in my career
I desperately wanted it not to.  I wanted to use is to test whether an 
expression was a valid arithmetic expression.  If is had failed silently
then I would have had a nice portable clean way of testing this.  As it was I
had to (sigh) program it all in myself and worry about what was allowable on
different systems and machines.

AS for 
	2 is Y+3
there are number of systems quite able to deduce that Y is 1.  This is the
right way to do it! Not an error message.  But I guess if we must standardize
to obsolete lanagugaes then we have to live in the past:-).

So come on some genius out there give us a good clean solution to this 
problem of errors.

	John G. Cleary
	Department of Computer Science
	University of Calgary
	2500 University Drive 
	N.W. Calgary
	Alberta T2N 1N4
	Canada

	cleary@cpsc.UCalgary.ca
	Phone: (403)282-5711 or (403)220-6087

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/30/90)

In article <1990Oct29.042755.18583@cpsc.ucalgary.ca>, cleary@cs-sun-fsc.cpsc.ucalgary.ca (John Cleary) writes:
> I very glad to see some real discussion of errors in Prolog.
> This topic is important and I have not yet seen any principled and
> acceptable solution.

What is unprincipled about the if_error/3+signal_error/1 scheme I
proposed to the BSI in 1984?  (The present exit_block/3+block/1 in
the current draft is my scheme plus an entirely incomprehensible
name change.)  What's wrong with the scheme in NU Prolog?  (The
difference between the NU Prolog scheme and my scheme is that my
scheme undoes variable bindings just like 'fail', while NU Prolog's
doesn't, the difference has to do with the possibility of constrained
variables appearing in ErrorTerms.)  What is unprincipled about the
scheme in ALS Prolog (catch/2 and throw/1; I'm not sure if it's like
the NU Prolog scheme or my 1984 scheme)?  There is a great deal to
dislike about the error handling stuff in IBM Prolog, but what don't
_you_ like about it?

> However, it is worth noting that the question is not as simple as it seems.
> Consider for example is.
> 
> Now
> 	X is fred
> should clearly generate an error message :-).  But at least once in my career
> I desperately wanted it not to.  I wanted to use is to test whether an 
> expression was a valid arithmetic expression.

In the context of user-extensible arithmetic (something that many ISO
contributors have wanted, and it has been part of several drafts) it is
NOT clear that
	X is fred
should generate an error message; it would be equivalent to
	fred(Y),
	should_be(number, Y, Y is fred),
	X = Y
and if for example
	fred(27)
happens to be true it would not be an error.

In my 1984 proposal to the BSI, this would have been done thus:

	if_error(_ is fred, _, fail)

I now believe that the ErrorTerm should be first (so that it is the
first argument of both the error-related commands).  Furthermore, we
only want to catch type failures.  So

	if_error(type_failure(_,_,_,_), _ is fred, fail)

Note that it is possible to make setting up an error handler very cheap.

You have raised an important point:  there are two alternative approaches
to error handling:  detection and prevention.  My favourite example is
opening files.  If there is an operation "would I be allowed to open
file F for operations O", then I don't need to handle errors in open/3.
But then we run up against the need for lots and lots of "would it be
ok to do X" predicates.

> As for 
> 	2 is Y+3
> there are number of systems quite able to deduce that Y is 1.

Any Prolog system that does that is ****WRONG****.  There are in fact
*INFINITELY* many solutions for Y: Y = 1, Y = 0+1, Y = 1*1, Y =
0+1*1, Y = 0*2+1*1, ...  If you want invertible addition, use
	plus(X, Y, X_plus_Y)

> So come on some genius out there give us a good clean solution to this 
> problem of errors.

Well, tell us what's wrong with the one we've got!
-- 
The problem about real life is that moving one's knight to QB3
may always be replied to with a lob across the net.  --Alasdair Macintyre.

pgl@cup.portal.com (Peter G Ludemann) (11/22/90)

[I'm posting this for Marc Gillet.]

------

In <4054@goanna.cs.rmit.oz.au> (Richard A. O'Keefe)  writes :

>I haven't been able to try this in IBM Prolog, but in one Prolog that
>I _have_ got that takes this kind of thing (the arity is part of the
>structure but the function symbol is not) there were two unpleasant
>effects:  first, the wretched thing didn't take the function symbol
>into account when indexing (I am *not* saying that IBM Prolog doesn't,
>not at all, but if you can't rely on the function symbol being _there_
>it's a wee bit tricky to index on it), and second precisely this
>anomaly occurred:
>        f =.. X         -> X = "f"
>        f() =.. X       -> X = "f"
>        T =.. "f"       -> T = f
>but NOT T =.. "f"       -> T = f()

>Why do I have to keep on saying that we want our EPs to satisfy
>SIMPLE laws?  Here are two laws which univ satisfies in Prologs whose
>data structures are based on the usual description of terms in
>first-order logic:

>        if at any instant T1 =.. L1, T2 =.. L2, L1 == L2 would succeed,
>        then at that instant T1 = T2 would succeed.
>       "univ is an isomorphism between terms and certain lists"

>       immediately after doing T1 =.. L1,
>       T2 =.. L1, T2 == T1 would succeed (where T2 is a new variable) and
>       T1 =.. L2, L2 == L1 would succeed (where L2 is a new variable)


In fact these problems don't exist in IBM Prolog.

IBM Prolog has two built-in predicates for =..

1) One called built2 : (T =.. L) is provided for compatibility with
   Edimburgh Prolog.

   This means that T has to be a functor with an atomic name or a
   constant and that functors of arity 0 are not allowed.

   This is the classical Edinburgh =.. and this predicate verifies
   the above properties.

   [You get built2 : (T =.. L) automatically when you are in
    Edinburgh syntax.]

2) The second is built : (T =.. L), in which T can be any
   functor, (including functor with non atomic names and functors
   of arity 0) but cannot be another term (variable excepted) like
   a constant. (It means that :- a =.. X . fails.)
 
   The above properties are equally verified for this predicate,
   because in IBM Prolog, the term X() does NOT unify with X.

   Furthermore :-  _ =..  L . succeeds for ANY non empty list L.

   The only difference is that:
   >       "univ is an isomorphism between terms and certain lists"
   has to be changed into :
     built : ( T =.. L) is an isomorphism between functors and non
     empty lists .

For the indexing, an easy solution is to apply the classical
indexing on the name and arity of a functor for all the
functors with an atomic name, and to not use indexing for the
other functors.  (That is to handle them like variables.)

Best Regards,
 Marc Gillet,