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,