Subject: Re: handling small numbers

From: "Richard A. O'Keefe" <[email-masked]>
Date: Wed, 10 May 2000 14:36:26 +1200 (NZST)

Considering the goal
    J is I + 1
where integer(I) is known to be true, Paul Sephton says

	It's not a wrong answer if you fail the goal- it's no answer at all 
	within the confines of the implementation.

That doesn't make sense to me.  The plain fact of the matter is that
if I is an integer in Prolog, then the goal J is I+1 ***has*** a
solution.  To fail the goal is to say that there is no solution,
but that is totally wrong.  There *is* a solution.

Whether a particular implementation can *represent* a solution is
not the same as whether there *is* a solution, and muddling these
two things up can only lead to confusion and grief (been there,
done that).

In exactly the same way, consider the goal
    functor(Term, '', N)
where integer(N), N >= 0 is known to be true.
Some Prologs put arbitrary limitations on N (100 in some versions of C
Prolog, 200 in the edai version of C Prolog, 127 in some Prologs, 255
in some, you name it, there's a crazy number out there *somewhere*).

Now if I have a Prolog program that *works* perfectly in a system
that supports arity 200, and I move it to a system that only supports
arity 100, what's going to happen?

With Sephton's approach to representation faults, the program will just
fail quietly and mysteriously, and I've got several days of debugging
ahead of me to find out why.

With the "exceeding representation limits is *not* failure but an
implementation-caused exception" approach, you get a an exception
(with a nice clear message in QP) the very first time you hit the
limit.

An important point here is that different Prologs have *different*
limits for integers.  I've see 14 bits, 16 bits, 18 bits, 24 bits,
28 bits, 30 bits, 32 bits, up 64 bits.  Letting an implementation
fail a *meaningful goal that has an answer* just because it feels
like it makes it much harder to port working code than it really
needs to be.

It is clear that for maximum utility, Prolog systems need to support
*at least* as many bits in their integer arithmetic as their host C
system does.  These days, that means 64 bits is the minimum.  But
only "at least".  There is no reason why Prolog should be limited to
"no more than" the C system's weird limits.

I've used Prolog systems with wraparound arithmetic (DEC-10 Prolog
for one).  The LONG.PL package exists because a geometry theorem
prover I "improved" broke completely due to PDP-11 Prolog's getting
its calculations hopelessly wrong (0-1 was a positive number in PDP-11
Prolog!).  

I note that an important part of the Mercury language (a descendant of
Prolog) is determinacy declaration and enforcement by the compiler.
Adopting the rule that + can fail whenever the implementation feels like
it would wreck Mercury's determinism checking.

	Were one to implement 
	arbitrary integer arithmetic within the compiler/interpreter, that 
	would be great, but hardly a requirement to sufficiently define the 
	language.
	
I can't parse that last phrase.
Lisp has bignum arithmetic.  Smalltalk has bignum arithmetic.
Erlang has bignum arithmetic.  Why *not* have bignum arithmetic in
the Prolog standard?  It's a solved problem, for heaven's sake!

	Would it not be more sensible to give the programmer the choice as to 
	whether he wants to use the exception mechanism to find out what the 
	reason was that the goal failed?

The exception mechanism is a mechanism for reporting *exceptions*,
not *failures*.  Presumably this means "would it not be more sensible
to give the programmer the choice as to whether he wants exceptions
or failures?".

Been there, done that.  The `unknown' flag in some Prologs controls what
happens when you call an undefined predicate.  People soon learned to switch
it onto the exception setting and *leave* it there all the time.

One problem here is the assumption that "the programmer" is ONE programmer.
Global flags saying what arithmetic does are a *nightmare* for people
writing library code.  In fact, global flags of any kind are a nightmare
in Prolog; I've written enough library code to wish them all in the dustbin.

In fact, if the fixed rule is that overflows raise exceptions,
then "the programmer" *DOES* have the power to say that some
overflows should be failures.  In the syntax I invented for QP (I shall
never forgive Quintus for yanking my paper on exception handling from a
conference), you'd write

	:- pragma(inline, [overflow_fails/1]).
	overflow_fails(Goal) :-
	    if_error(representation_fault(_), call(Goal), fail).
	    % ISO Prolog uses `catch', a rather silly choice.

in your own library, and then use

	    overflow_fails(J is I+1)

to get the effect Sephton wants.  I have no problems with explicit local
overrides of the default.

	Just coming back to my example of the number_chars/2 predicate-  I really 
	don't care that the predicate fails due to a "representation error", only 
	that whatever it was I was trying to convert was not a valid 
	representation for a number.

Yes you do.  Representation fault means that *you* didn't make a mistake,
the system has a limit.  If you were trying to convert (what do you mean?)
something that was not a valid representation for a number, that's an
error, which should be reported in *all* Prologs.  The threshold at which
you get a representation fault will *vary* with different Prologs.
If I get a program working in Prolog system X, and send it to you to use
in Prolog system Y, you really really want to know whenever it matters
that I assumed 64-bit arithmetic but you only have 32-bit arithmetic.

	Simply, the way I think things should work here, would be for integer 
	promotion to be an optional feature.

Once again, optional features are death to portability.

One of the reasons Smalltalk has a good reputation in financial circles 
(or so I am told, but I was told so by someone from a bank wanting to know
whether we taught it because they needed more Smalltalk programmers)
is that there was a day when a lot of stock exchange programs written in
C started giving insane answers, but the Smalltalk programs just soldiered
on.  The C programs were using wraparound arithmetic, and the Smalltalk
ones were using bignums.

Switching over from integer to floating point without the programmer's
*explicit* permission has the same porting problems as quietly lyingly
failing.  Different Prolog systems will have different thresholds.
If a Prolog system supporting 64-bit integers (for compatibility with
C) switches over to IEEE doubles, it will lose information.

	With it, one would not be 
	guaranteed absolute accuracy, but would have the benifit of having all of 
	your hair after reading the source code.
	
I do not understand the reference to hair here.
This seems to be completely back to front.
With a *silent* switchover to float, your program starts giving
wrong answers when you port it, and you have *NO* idea where it
happened.  With an exception, finding the problem could hardly be
simpler.

	Without the feature, one may still retain ones hair whilst accepting that 
	integer overflow would cause the goal to fail normally,

As I've argued above, quiet failure for integer overflow is unforgivable.

	Yes, you are right of course.  But programmers _should_ be able (and 
	allowed) to use their alleged intelligence to decide whether or not it 
	matters that the query has an answer which the implementation is too 
	limited to provide.
	
Indeed, AND USING THE EXCEPTION MECHANISM IS WHAT LETS THEM DO THAT.
It is
    - silently failing a goal because the implementation doesn't feel
      like giving the right answer
and
    - silently switching over to a different algebra just because the
      implementation doesn't feel like giving the right answer
which take away the programmer's power to use his or her intelligence.

Here we are:

    :- pragma(inline, [addf/3]).
    :- pragma(use_in_arithmetic, [addf/3]).
    addf(X, Y, A) :-
        if_error(representation_fault(_), T is X+Y, T is float(X)+float(Y)),
        A = T.

(I'm agreeing here that a mechanism for adding user-defined predicates
 into arithmetic is a Good Thing.  Mercury's functional sublanguage is
 a better way to go.)

This lets you use I+1 when you want the right answer,
and addf(I,1) when you are willing to tolerate some fuzz.
It is *THIS* which lets a programmer exercise intelligence;
*NOT* silent implementation-dependence diktats.

	My example was assuming your "non-promotion of integers" stance, but 
	without the apparently consequent need for the introduction of an 
	implicit exception handling mechanism.

But there *is* an exception handling mechanism in ISO Prolog.
From the SWI Prolog 3.3.4 builtin.doc:

    \section{ISO compliant Exception handling}   \label{sec:exception}

    SWI-Prolog defines the predicates catch/3 and throw/1 for ISO compliant
    raising and catching of exceptions. In the current implementation
    (2.9.0), only part of the built-in predicates generate exceptions. In
    general, exceptions are implemented for I/O and arithmetic.
             ******************************         **********

An exception handling mechanism is needed in Prolog anyway, as I pointed
out to the BSI committee, with a proposal, in 1984 (and I think a
commercial Prolog may have implemented it even before that, which I
could remember which).  There is one in ISO Prolog.  It is supported in
SWI Prolog.  There is no need to "introduce" something which already
exists.

There *is* a need to use it!

	In my opinion a goal that fails and backtracks is just another
	example of an exception- ok, not your idea of an exception, but
	nevertheless.
	
That's not how the ISO standard uses the term, nor how the SWI Prolog
documentation uses it.