Proposal: [32] The value of STATE should be restored


This proposal has been moved into this section. Its former address was: /standard/exception/THROW

This page is dedicated to discussing this specific proposal


AlexDyachenkoavatar of AlexDyachenko [32] The value of STATE should be restoredProposal2017-09-03 11:07:49

To meaningfully continue after a THROW/CATCH, the value of STATE should be captured during CATCH and restored during THROW.

LeonWagneravatar of LeonWagner

What is your use case for this? Could you show us an example?

AntonErtlavatar of AntonErtl

We discussed this at the standards meeting, but do not have an answer for this proposal yet. Anyway, here is my personal answer:

It is a common problem to have a global variable that you want to restore on exiting a word, whether the exit is regular, or through THROW. This problem exists not just for system variables like STATE or BASE, but also for application variables. The general approach that I recommend is to have wrappers around the code that changes the variables. The wrapper catches any exception coming through, restores the old value of the variable, and then THROWs the exception (if any) on. For STATE, this can be done as follows:

: state! ( f -- )
  if ] else postpone [ then ;
: state-wrapper ( xt -- )
  state @ >r catch r> state! throw ;

\ usage example
s" ] non-existent-word" ' evaluate ' state-wrapper catch .

AlexDyachenkoavatar of AlexDyachenko

My main use case was trying to handle any errors that may occur when including another file, e.g.

S" somefile.4th" ' INCLUDED CATCH ...

This code could be interpreted or compiled, and of course inside somefile.4th the STATE can change arbitrarily prior to a THROW. So anything following CATCH would only work as intended if STATE had not changed. Your example is certainly one way to work around this, but it requires every use of CATCH to be wrapped. In my opinion, if CATCH does not restore STATE, it falls short of satisfying this from the THROW Rationale: "If THROW is executed with a non zero argument, the effect is as if the corresponding CATCH had returned it." The application variables are certainly the application's responsibility, but system variables should be the system's.

If an otherwise standard system chose to restore STATE and BASE as part of the CATCH semantics, would this still be a standard system?

AntonErtlavatar of AntonErtl

Not every CATCH needs to be wrapped, only code that changes STATE. If you have a word that changes and has to restore STATE, use a STATE-wrapper. All other CATCHes are unaffected. Because the INCLUDED can change STATE, use a STATE-wrapper for the INCLUDED in your example.

The cited statement from the rationale is about what happens on the stack, and is unrelated to other state.

A system that saves and restores STATE on a CATCH would not be standard, because it would do programmer-visible things that are not specified in the standard.

StephenPelcavatar of StephenPelc

The current definition of CATCH and THROW is deliberately minimal. I believe that it should stay minimal. An embedded system may use CATCH/THROW but not have STATE or BASE.

We (MPE) have attempted in the past to cope with customer wishes for extended facilities. 30 years later we regret all these concessions. The best that we could do now is to DEFER CATCH and THROW.

In the given use case (INCLUDED and friends), the most likely action of the final error handler is to leave compilation and perform some form of restart. We also have plenty of instances in which errors are rethrown because the CATCH/THROW mechanism itself is used to ensure state recovery at several levels. In this case, putting a CATCH inside INCLUDED and then throwing again permits STATE restoration, and no specification change for CATCH is required.

JimPetersonavatar of JimPeterson

The best that we could do now is to DEFER CATCH and THROW.

Are CATCH and THROW compatible with DEFER! / DEFER@ ? I have concerns about reserving resources that won't be released with the given specification for CATCH/THROW, but those concerns would be alleviated if I could interject code via hooks into standard CATCH/THROW mechanics.

Simply redefining CATCH/THROW would be problematic, of course, given that previous code might already have the older definitions baked in, and could surround redefined versions... The more I think about it, the more I believe the standard should prohibit (or at least discourage) redefinitions of CATCH and THROW.

ruvavatar of ruv

Simply redefining CATCH/THROW would be problematic, of course, given that previous code might already have the older definitions baked in,

It's enough to redefine catch. And it isn't problematic at all, since use of catch by the system is not detectable by a program.

For example, you can restore base in your program:

: catch ( i*x xt -- j*x ior ) base @ >r catch dup if r> base ! exit then rdrop ;

(NB: the system is non standard after that).

Restoring state doesn't have much sense in a standard program at the moment, since in any case, if compilation was broken, the program cannot create a new definition due to an ambiguous condition, and cannot complete the current definition since the control-flow stack is cleared.

OTOH, saving and restoring (or releasing) some state/resource in every catch is overhead. A better way, as Anton pointed out, is to use a specific save/restore in the places where it is required only.

Reply New Version