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.