[comp.lang.prolog] Trapping interrupts cleanly

POPX@vax.oxford.ac.uk (Jocelyn Paine) (05/31/91)

Newsgroups: comp.lang.prolog
Subject: Trapping interrupts cleanly
Summary:
Expires:
Sender:
Reply-To: popx@vax.ox.ac.uk (Jocelyn Paine)
Followup-To:
Distribution: comp.lang.prolog
Organization: Experimental Psychology, Oxford University, UK.
Keywords:

I'd be interested to know whether there are any Prologs that handle
interrupts in a way that doesn't destroy functional purity. Let me
explain. I have a command-interpreter written in Prolog whose main loop
is of the form
    mainloop :-
        read_command( C ),
        obey( C ),
        mainloop.

The interpreter has to maintain some internal state (if it didn't, it
would be about as interesting as those Prolog systems that reply "no"
regardless of what you ask them). This state is interrogated by some
commands, and can be changed by others. How to represent this? The dirty
way would be as clauses. To change state, obey would retract and
reassert one or more of these clauses.

The clean way is by holding the state as an argument
    mainloop( State ) :-
        read_command( C ),
        obey( C, State, NewState ),
        mainloop( NewState ).
where NewState is the effect of command C acting on State. I suppose all
Prolog implementations _are_ now capable of tail-recursion-optimising
this kind of loop and of garbage collecting the inacessible components
of State?

But the need to trap interrupts seems to destroy this purity. Some of
the commands are listing commands: they may generate several screenfuls
of output. I want users to be able to interrupt them with a CONTROL-C
(I'm using a VAX), and then to have my interpreter call mainloop with
whatever State was in force when the interrupt occurred.

Now, I'm using Poplog Prolog. In this, you can nominate a predicate to
be obeyed whenever CONTROL-C is typed. (For those who know Pop-11, you
do so by assigning to the system procedure "interrupt"). But the only
way to pass the State to the interrupt predicate is by doing something
like
    mainloop( State ) :-
        read_command( C ),
        retractall( interrupt_goal(_) ),
        assert( interrupt_goal( mainloop(State) ),
        obey( C, State, NewState ),
        mainloop( NewState ).
and arranging for the interrupt predicate to do
        interrupt_goal( G ),
        call( G ).

This enforces an assert/retract pair on each cycle. If I have to do
that, I might as well go back to representing the whole state as
database assertions!

The only other Prolog I've used extensively is ESL Prolog-2, and it
doesn't provide any cleaner solution to this problem. Are there any
Prologs that do? What is considered good style on those that don't?

                                    Jocelyn Paine

dahmen@ecrc.de (Michael Dahmen) (06/03/91)

> Article 3462 in comp.lang.prolog:
> From: popx@vax.ox.ac.uk (Jocelyn Paine)

> I'd be interested to know whether there are any Prologs that handle
> interrupts in a way that doesn't destroy functional purity.

> Now, I'm using Poplog Prolog. In this, you can nominate a predicate to
> be obeyed whenever CONTROL-C is typed. (For those who know Pop-11, you
> do so by assigning to the system procedure "interrupt"). But the only
> way to pass the State to the interrupt predicate is by doing something
> like
>     mainloop( State ) :-
>         read_command( C ),
>         retractall( interrupt_goal(_) ),
>         assert( interrupt_goal( mainloop(State) ),
>         obey( C, State, NewState ),
>         mainloop( NewState ).
> and arranging for the interrupt predicate to do
>         interrupt_goal( G ),
>         call( G ).

> The only other Prolog I've used extensively is ESL Prolog-2, and it
> doesn't provide any cleaner solution to this problem. Are there any
> Prologs that do? What is considered good style on those that don't?

I suggest to use a non-local exit construct (block/3, exit_block/1).

mainloop( State ) :-
        read_command( C ),
        block(obey( C, State, NewState ), abort, State = NewState),
        mainloop( NewState ).

and arranging for the interrupt predicate to do
       exit_block(abort).

I am not sure, but I think block/3 and exit_block/1 are part of the 
proposed Prolog standard, and many Prolog implementation already
include them.

[Semantics: block(Goal, Tag, Recovery) calls Goal. If during evaluation
of Goal an exit_block(ExitTag) occurs, such that ExitTag and Tag unify,
then Recovery is called. Afterwards execution continues as if block/3
has been replaced by call(Recovery). If no exit_block/1 occurs, block/3
behaves like once(Goal).]

-- Michael Dahmen, ECRC, Munich

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

In article <9105310922.AA22183@ucbvax.Berkeley.EDU>, POPX@vax.oxford.ac.uk (Jocelyn Paine) writes:
> I'd be interested to know whether there are any Prologs that handle
> interrupts in a way that doesn't destroy functional purity.

I don't see how it can possibly be done.
If you want something in the spirit of Prolog in which it _can_ be
done cleanly, look at the LOGIX implementation of FCP, or at Strand,
or Parlog, or something like that.

But what you want is very definitely a way of causing a computation
to be aborted (if you just want to stop output to a terminal, doesn't
VMS have a ^O keyboard command? I thought Berkeley got it from DEC).
That's not something that can readily be done in Prolog-as-we-know-it
while maintaining "purity".  You might be able to adapt some ideas
from the functional programming people; treat every interaction with
the "operating system" as _terminating_, but give the OS one or more
"continuations" it can run.


-- 
Should you ever intend to dull the wits of a young man and to
incapacitate his brains for any kind of thought whatever, then
you cannot do better than give him Hegel to read.  -- Schopenhauer.

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

In article <1991Jun3.071610.23645@ecrc.de>, dahmen@ecrc.de (Michael Dahmen) writes:
> I suggest to use a non-local exit construct (block/3, exit_block/1).
> I am not sure, but I think block/3 and exit_block/1 are part of the 
> proposed Prolog standard, and many Prolog implementation already
> include them.

The operations were proposed to the BSI in late 1984 under the rather
less obscure names (I often use T, and so for me block=begin=progn)
if_error/3 and signal_error/1.  They are present in Quintus Prolog
under slightly different names.  ALS Prolog has them as `catch' and
`throw', which fits the cultural context.  NU Prolog also has them.
IBM Prolog has a similar feature.  No doubt there are many others.
block/3 and exit_block/1 are by far the very worst names I've seen.
There is a subtle difference between NU Prolog's and the version
proposed in 1984.  The 1984 version undoes bindings; NU Prolog's
equivalent does not.  That is due to the interaction of exception handling
with coroutining.  I now think that it would be most unwise for the ISO
committee to include these operations in the standard until they have
understood the interaction between exception handling and coroutining.
(For example, in the presence of coroutining, it need not be true that
signalling an exception results in the ``block'' (what's a block) being
``exited''.  Scheme programmers who use full continuations are only too
painfully aware of the kind of thing that can happen.  The price we pay
for power!)

> [Semantics: block(Goal, Tag, Recovery) calls Goal. If during evaluation
> of Goal an exit_block(ExitTag) occurs, such that ExitTag and Tag unify,
> then Recovery is called. Afterwards execution continues as if block/3
> has been replaced by call(Recovery). If no exit_block/1 occurs, block/3
> behaves like once(Goal).]

That is obscene.  Quintus's implementation of the 1984 proposal has
	if_exception(ErrorDescription, Goal, Handler)
acting like
	call(Goal)
in the absence of an exception, and like
	(E = ErrorDescription -> call(Handler) ; signal_exception(E))
if an error described by the term E occurs.  There is no shadow of a
trace of an excuse for making if_error/3 act like once/1.  (No, that
does _not_ "fix" the interaction with coroutining.)

-- 
Should you ever intend to dull the wits of a young man and to
incapacitate his brains for any kind of thought whatever, then
you cannot do better than give him Hegel to read.  -- Schopenhauer.