[comp.lang.prolog] block and exit_block

ted@nmsu.edu (Ted Dunning) (06/06/91)

even though this is the response to an email message, i think it
should be given wider circulation.

   Date: Tue, 4 Jun 91 08:13:31 +0200
   From: Michael Dahmen <dahmen%ecrc.de>

   > why in the world do they use the names block/3 and exit_block/1?
   > why not the much more customary catch and throw?

   Maybe because they don't known or don't like LISP ... :-)

isn't this a wonderful motivation for making a proposal for an iso
standard?  gives me shivers of awe thinking about the deliberations of
that august standards body working on my behalf and on the behalf of
all the other little people in the world of prolog users.

i suppose that they would object to naming something unwind_protect on
the same grounds.

i understand doing something differently in a new language because it
was done poorly before.  but i have a hard time understanding a change
in the name _only_ in order to be different, especially when the
previous implementation worked pretty well.  this all sounds pretty
childish.

block and exit_block sound like they refer to blocks.  blocks are very
commonly (from algol onward) to refer to static reference scopes, not
installing handlers with dynamic extent.

how about handle_exception and raise_exception, or handle_event,
raise_event or any number of _descriptive_ names.


--

		When in doubt, take the trick.

				Hoyle & Hoyle (quoting Hoyle)

dave@quintus.UUCP (David Bowen) (06/08/91)

In article <TED.91Jun6083527@kythera.nmsu.edu> ted@nmsu.edu (Ted Dunning) writes:

>   Date: Tue, 4 Jun 91 08:13:31 +0200
>   From: Michael Dahmen <dahmen%ecrc.de>
>
>   > why in the world do they use the names block/3 and exit_block/1?
>   > why not the much more customary catch and throw?
>
>   Maybe because they don't known or don't like LISP ... :-)
>
>isn't this a wonderful motivation for making a proposal for an iso
>standard?  gives me shivers of awe thinking about the deliberations of
>that august standards body working on my behalf and on the behalf of
>all the other little people in the world of prolog users.

I don't know where the names came from.  However, the U.S. committee (X3J17)
recommended changing them to catch and throw and this was agreed upon at the
last meeting of the ISO committee (WG17) last November.

The committees working on Prolog standardization are not particularly august.
They are mostly made up of people who have full-time jobs doing other things,
and thus it takes a while for even obviously bad ideas, like the names block
and exit_block, to be cleared up.  (How those names got in the draft in the
first place I do not know.)

dc@dcs.qmw.ac.uk (Daniel Cohen;E303) (06/10/91)

In <1536@quintus.UUCP> dave@quintus.UUCP (David Bowen) writes:

>In article <TED.91Jun6083527@kythera.nmsu.edu> ted@nmsu.edu (Ted Dunning) writes:

>>   Date: Tue, 4 Jun 91 08:13:31 +0200
>>   From: Michael Dahmen <dahmen%ecrc.de>
>>
>>   > why in the world do they use the names block/3 and exit_block/1?
>>   > why not the much more customary catch and throw?
>>
>>   Maybe because they don't known or don't like LISP ... :-)
>>
>>isn't this a wonderful motivation for making a proposal for an iso
>>standard?  gives me shivers of awe thinking about the deliberations of
>>that august standards body working on my behalf and on the behalf of
>>all the other little people in the world of prolog users.

>I don't know where the names came from.  However, the U.S. committee (X3J17)
>recommended changing them to catch and throw and this was agreed upon at the
>last meeting of the ISO committee (WG17) last November.

And the latest draft of the standard ( N72 ) refers to catch and throw, so
it looks like this one has been cleared up, at least until Paris when
someone will no doubt try to change the names back again!

--
Daniel Cohen              Department of Computer Science 
Email: dc@dcs.qmw.ac.uk   Queen Mary and Westfield College
Tel: +44 71 975 5249/4/5  Mile End Road, London E1 4NS, UK
Fax: +44 81 980 6533      *** Glory, Glory, Hallelujah ***

bimbart@hera.cs.kuleuven.ac.be (Bart Demoen) (06/11/91)

perhaps some people are interested in my KUL-CW report 97:
	"A 20' Implementation of Catch and Throw in WAM"
it describes the implementation of block/exit_block (or catch/throw if you
prefer) as defined by ISO

Bart Demoen

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/13/91)

In <1536@quintus.UUCP> dave@quintus.UUCP (David Bowen) writes:
> I don't know where the names came from.  However, the U.S. committee (X3J17)
> recommended changing them to catch and throw and this was agreed upon at the
> last meeting of the ISO committee (WG17) last November.

I believe the names were proposed by AFNOR.  The earlier proposal (for
if_error/3 and signal_error/1) failed, having had an Auckland postmark.

In article <dc.676561471@guinness>, dc@dcs.qmw.ac.uk (Daniel Cohen;E303) writes:
> And the latest draft of the standard ( N72 ) refers to catch and throw, so
> it looks like this one has been cleared up, at least until Paris when
> someone will no doubt try to change the names back again!

I believe that ALS are represented on the ANSI committee, and the last time
I saw a manual for ALS Prolog on the Mac it called the operations 'catch'
and 'throw'.  Surprise!  (The "Personal" version on the PC lacks these
operations.)  The names are marginally better than `block' and
`exit_block', but they really are not at all good.

Let me start by pointing out that in Common Lisp and MIT Scheme and other
modern Lisp-based systems a very sharp distinction is drawn between
catch/throw and exception handling.  catch/throw or escape functions
(Pop2 "jumpouts") are  for non-local transfers of successful control.
Exception handling is for handling exceptions.  An exception handler
may indicate *recovery* by executing a throw (calling an escape function),
but error *signalling* is most definitely *not* based on catch/throw.
Indeed, my use of structured terms to represent error situtations in the
Quintus design was directly inspired by the proposal which eventually
became the basis of the present Common Lisp "condition" system.
The parallel goes something like this:

	QP			CLtL2
	signal_exception	error		[p886]
	if_exception		handler-bind	[p898]
	<error term>		<condition>	[p901]

There's nothing quite like CL's "restarts".  Perhaps there should be.
The way an interactive user can select a restart in, say, Oaklisp,
is rather neat.

I've tried to keep my Lisp skills reasonably current.  I believe that
a good AI programmer will not lock himself into one programming language,
even if it is as good as Prolog, so that if he comes across an excellent
program written in the "other" language he'll still be able to learn
from it.  I claim that my experience with Scheme and Lisp has conditioned
me to expect that 'throw' is like selecting an alternative SUCCESS
continuation, whereas my knowledge of exception handling systems (such as
"recovery blocks" and "restarts") is that arriving in an exception handler
should be like selecting an alternative FAILURE continuation.  To put one
consequence of this plainly, I expect that

	var(X), catch(*, (X=1, throw(*)))
	=> X == 1

but that

	var(X), if_exception(*, (X=1, signal_exception(*)), true)
	=> var(X)

If something called catch/throw *does* undo my variable bindings, I am
going to be _very_ annoyed, and I will write nasty letters to the
vendor in question for providing me with a useless implementation of
non-local exit.
If something advertised as an exception handling mechanism *doesn't*
restore my variable bindings to the state they were in before the
protective was donned, I am going to be _very_ _very_ annoyed indeed,
and I will demand my money back.

In 1984 I proposed as a guiding principle for any Prolog standard
that it should contain nothing that would _forbid_ a coroutining
implementation.  Frank McCabe did announce in 1985 that the BSI
committee had a guiding principle:  to produce a minimal standard.
But the "don't kill MU Prolog" principle was not adopted.  None-the-
less, I still think it's a good principle.

NU Prolog and other coroutining Prolog systems pose an extremely
interesting question.  Suppose I do
	signal_exception(foo(X))
at a time when X is _constrained_ (by one or more delayed goals) but
not _instantiated_?  *EXCEPTION* handling means that it makes no sense
to continue the current conjunct, so we mustn't wake up the delayed
goals.  What to do?  For catch/throw, because we're selecting a new
continuation point in the *same* conjunct, it does make sense to
continue, and we can wake up the delayed goals when we need to, but
then we can't use the mechanism for recovery, and we cannot use the
mechanism to abandon a computation that is known to be useless, because
any amount of it could be persisting as suspensions.  What to do?

I'm really not sure what the "right" answer is.  If someone wants to
provide catch and throw in a Prolog system, the only problem is that
it can't be used as an exception handling/recovery mechanism.  For
exception handling, The only suggestion I have is that in QP,
if storage permits,
	signal_exception(X)
and
	( assert('Snark'(X), Ref),
	  instance(Ref, 'Snark'(Y)),
	  erase(Ref),
	  signal_exception(Y)
	)
behave the same, so that signal_exception/1 in a coroutining system
should treat constrained but uninstantiated variables the same way
that assert/1 does (or, by a similar argument, the way write/1 does).
I hope someone else has a better idea.
-- 
Q:  What should I know about quicksort?   A:  That it is *slow*.
Q:  When should I use it?  A:  When you have only 256 words of main storage.

bimbart@hera.cs.kuleuven.ac.be (Bart Demoen) (06/14/91)

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

> If something called catch/throw *does* undo my variable bindings, I am
> going to be _very_ annoyed, and I will write nasty letters to the
> vendor in question for providing me with a useless implementation of
> non-local exit.

catch/throw as currently defined by WG17, undo the variable bindings; still,
that doesn't make them useless for an alternative SUCCESS continuation,
because through the ball you 'throw' and its unification with the catcher,
you can pass any information from deep down, higher up; e.g.

	var(X), catch((X=1, throw(X)),X,true)
	=> X == 1

it doesn't look very nice, but at least you have the option

Bart Demoen

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/15/91)

In article <3855@n-kulcs.cs.kuleuven.ac.be>, bimbart@hera.cs.kuleuven.ac.be (Bart Demoen) writes:
> perhaps some people are interested in my KUL-CW report 97:
> 	"A 20' Implementation of Catch and Throw in WAM"
> it describes the implementation of block/exit_block (or catch/throw if you
> prefer) as defined by ISO

Perhaps it might be a good idea to send a list of logic-programming-related
KUL-CW reports to comp.lang.prolog, together with instructions for getting
them.  I know that _I_ would be interested in seeing such a list.
-- 
Q:  What should I know about quicksort?   A:  That it is *slow*.
Q:  When should I use it?  A:  When you have only 256 words of main storage.

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/18/91)

In <6245@goanna.cs.rmit.oz.au> I wrote
> If something called catch/throw *does* undo my variable bindings, I am
> going to be _very_ annoyed, and I will write nasty letters to the
> vendor in question for providing me with a useless implementation of
> non-local exit.

In article <3946@n-kulcs.cs.kuleuven.ac.be>,
bimbart@hera.cs.kuleuven.ac.be (Bart Demoen) writes:
> catch/throw as currently defined by WG17, undo the variable bindings; still,
> that doesn't make them useless for an alternative SUCCESS continuation,
> because through the ball you 'throw' and its unification with the catcher,
> you can pass any information from deep down, higher up; e.g.
> 
> 	var(X), catch((X=1, throw(X)),X,true)
> 	=> X == 1
> 
> it doesn't look very nice, but at least you have the option.

No, I'm afraid that doesn't do the job.  It can only restore the
bindings of variables that (occur in the bindings of variables
that) are visible at the point of the throw.  If I do

	var(X), catch(Ball, (X = 1, p(Ball)), true)

there is no definition I can give to p/1 which will result in the
value of X being preserved.

My main points remain:
(1) The Lisp community, from which the names `catch' and `throw'
    were ``borrowed'', distinguishes between non-local control transfer
    (in Scheme, this is analogous to backtracking) and exception
    handling, and catch/throw are NOT the exception handling tools.
    The kind of unnecessary confusion this introduces (I'm reading
    a Mach manual set at the moment, and `catch' does _not_ refer to
    exception handling there either) could be compared, say, to
    forcing people to say ``interrupt'' instead of ``system call''.
    If we use operation names borrowed from another WIDELY KNOWN
    language or family of languages, we should not be so discourteous
    as to assign meanings to them so greatly at variance with what
    they meant in their ``home'' language.  (A linguistic parallel:
    I believe `divan' originally meant a bound book of poems.)

(2) The interaction of exception handling with coroutining needs to
    be explicitly addressed.  It is _not_ a trivial generalisation of
    the strictly ordered case.

I would again suggest that the dynamic-wind construct found in many
Schemes would repay study.

-- 
Q:  What should I know about quicksort?   A:  That it is *slow*.
Q:  When should I use it?  A:  When you have only 256 words of main storage.

ward@vlsi.waterloo.edu (Paul Ward) (06/18/91)

Could someone please explain, briefly and with examples, exactly what
catch and throw are and when and where they might be used, and what
relationship they have to logic.

Paul Ward
University of Waterloo
-- 
They will say, "As surely as the LORD lives, who brought the Israelites up out
of the land of the north and out of all the countries where he had banished
them."  For I will restore them to the land I gave to their forefathers.
                                                                Jeremiah 16:15

bimbart@hera.cs.kuleuven.ac.be (Bart Demoen) (06/19/91)

In <6378@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe)
wrote:

> No, I'm afraid that doesn't do the job.  It can only restore the
> bindings of variables that (occur in the bindings of variables
> that) are visible at the point of the throw.  If I do
> 
> 	var(X), catch(Ball, (X = 1, p(Ball)), true)
> 
> there is no definition I can give to p/1 which will result in the
> value of X being preserved.

that's correct and I didn't imply that you wouldn't have to rewrite - in
particular: add arguments to - some of the predicates in your program;
in the example above, X should be visible at the point of the throw, so
you have to make it a parameter of p - that's why I said that ot doesn't
look nice

I appreciate Richard's comments about the distiction between non-local
control transfer and exception handling, but it seems to me that Prolog
can do both with just one mechanism - or one pair of builtin predicates;

and for me, there remains the question: should WG17 try to standardize
non-local control transfer and exception handling ?

Bart Demoen

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

In article <1991Jun18.125519.7422@vlsi.waterloo.edu>, ward@vlsi.waterloo.edu (Paul Ward) writes:
> Could someone please explain, briefly and with examples, exactly what
> catch and throw are and when and where they might be used, and what
> relationship they have to logic.

Which catch and throw do you mean?  Do you mean the ``non-local return to
a label with dynamic scope'' things in Lisp-like languages, or the
``Ada-like exception handling things with misleading names'' proposed for
ISO Prolog?

The basic point is that in real-world programming you come up with
situations where it is not possible to succeed with any binding, but
it would be wrong to fail.  Consider, for example,
	get0(C)
issued after the last character of the file has been consumed.
You can't suceed, because there isn't any character to bind C to.
But you can't fail, either, because failure would mean "I _did_
read a character, but it didn't unify with C".  This is an instance
of what I call an ``existence error''; the program has asked for
(some attribute of) a well-identified object which doesn't exist.
(Trying to open a non-existent file for input is another.)

The scheme that I proposed to the BSI at the end of 1984 was
	if_error(Goal, ErrorTerm, Handler)
	signal_error(ErrorTerm)
It is now clear that the argument order for if_error was at the very
least an error of taste.  The ErrorTerm should be in the _same_
argument position in both commands (and in other commands related to
exception handling).  Quintus decided that 'exception' was a better
name than 'error'.  So we have

	if_exception(ErrorTerm, Goal, Handler)

		acts exactly like Goal, unless a call to signal_exception
		occurs while Goal is running, in which case Goal is
		abandoned, variables are reset to the state they had
		before if_exception was started (this includes variables
		in ErrorTerm).  If the error term that was signalled
		unifies with ErrorTerm, the Handler is executed in place
		of the Goal.  (The Handler is _not_ regarded as ``inside''
		the if_exception form; any errors in it will _not_ result
		in the Handler being restarted.)  If the error term that
		was signalled does not unify with ErrorTerm, the error is
		in effect resignalled.

	signal_exception(ErrorTerm)

		makes of copy of ErrorTerm, then keeps on failing until
		it finds an _ancestor_ if_exception(E, Goal, Handler)
		where E unifies with the copy of ErrorTerm.

		I describe this as "failing into the success continuation".

Many built in operations may signal an exception.  For example, floating-
point overflow should signal a representation fault.  There is a mathematically
defined answer, but the system can't represent it, so can't succeed, but it
would be wrong to fail because the answer does exist.

Concerning the relation to logic:

	every solution of if_exception(E,G,H)
	is either a solution of G
	or a solution of (E=T,H) for some term T.

	every solution of var(X), if_exception(X,G,H), var(X)
	is a solution of G.

	every solution of var(X), if_exception(X,G,H), nonvar(X)
	is a solution of (X=T,G) for some term T, where T does
	not share variables with any other term.

	if_exception(E, G, H)
	is identical in effect to
	if_exception(X, G, (X = E -> H ; signal_exception(X))

As a tiny example of exception handling:

	open_input_file(Name0, Name, Stream) :-
		if_exception(existence_error(_,_,_,_),
		    (	open(Name0, read, Stream),
			Name = Name0
		    ),
		    (	format('~&~w does not exist.  Try another name.~%',
				[Name0]),
			read(Name1),
			open_input_file(Name1, Name, Stream)
		    )).

There are many other things that can go wrong with a call to open/3,
but an existence error means that you had a file name that made sense
but failed to name an existing file.


Oh yes, one point.  A pons asinorum of exception handling is to mistake
them for a way of handling interrupts.  Now an interrupt handler might
well decide to signal an exception, but you wouldn't, for example, want
to handle SIGCONT that way...
-- 
Q:  What should I know about quicksort?   A:  That it is *slow*.
Q:  When should I use it?  A:  When you have only 256 words of main storage.