On 1/8/22 12:27, Anton Ertl wrote:
> Krishna Myneni <
krishna...@ccreweb.org> writes:
>> On 1/3/22 07:39, Anton Ertl wrote:
>>> Krishna Myneni <
krishna...@ccreweb.org> writes:
>>>> Within a definition, the typical way in which exceptions may be caught
>>>> for error handling is to use the following sequence.
>>>>
>>>> ['] WORDTOTRY CATCH ?DUP IF ... THEN
>>>
>>> My typical usage of CATCH is for cleaning up, and it follows the
>>> scheme
>>>
>>> save-state [: change-state do-something ;] catch restore-state throw
>>>
>>> Of course it's also possible that you want to handle a particular
>>> exception, and then you need to look at the ball instead of just
>>> throwing it onwards.
>>>
>> ...
>>
>> I assume your use of words SAVE-STATE and RESTORE-STATE above goes
>> beyond just the information CATCH saves within the exception frame
>> pushed onto the exception/return stack and THROW popping an exception
>> frame and restoring stack pointers.
>
> Yes, of course. This pattern is there exactly for making sure that
> state is restored that CATCH does not restore by itself. A more
> concrete example:
>
> : hex. ( u -- )
> base @ >r [: hex u. ;] catch r> base ! throw ;
>
Ok, this is a good example. At first I was thinking, why would the code
in the quotation throw an exception, but U. can perform a throw if
nothing is on the stack.
>> The source code implementation of SLURP-FILE which I posted in this
>> thread illustrates precisely why THROW is not terribly useful by itself
>> for communicating additional information beyond the throw code. For
>> example, if SLURP-FILE successfully opens the file, but fails on the
>> MAX_SLURP test and throws an error code of 1, there is no reliable way
>> to communicate the file id using only THROW so that the error handler
>> defined by CATCH can close the file.
>
> The way to do that would be to write slurp-file as
>
> : slurp-file
> ... open-file throw dup >r [: ... ;] catch r> close-file throw throw ;
> That eliminates the need to communicate the file-id further out and
> let some far-out catch deal with all sorts of trouble that that
> handler is probably badly prepared to deal with.
>
If you only want to close the file on an error, yes, that's best done at
the earliest point where the error is detected. Within the SLURP-FILE
example, this would be best done, for example, as
...
file-size
if slurp_fid @ close-file drop -66 throw
then
0<> over MAX_SLURP > or
if slurp_fid @ close-file drop 1 throw \ File too large
then dup slurp_size ! allocate
if slurp_fid @ close-file drop -59 throw
...
However, this still does not allow for us to pass adequate information
for handling throw code 1, when the actual file size is larger than the
initial setting of MAX_SLURP.
Let us assume we have a module (a library with a well-defined interface
along with information private to the library) which includes SLURP-FILE
and SET-MAX-SIZE as interface words. Despite the default setting of the
library, the handler could respond to throw code 1 by looking at the
available memory in the system, at that particular instance, compared to
the file size, and decide whether or not there is enough memory
available to safely increase the max size to accommodate the file. In
such a case, the program can retry to slurp the file; otherwise it can
throw an error farther up the call chain.
Of course there are other ways to organize the program so that checks
are done prior to calling SET-MAX-SIZE and SLURP-FILE, but if this
scenario is an exception (not likely to occur frequently), then an
exception handler equipped to deal with the problem is justified.
Global variables, structures, per thread instances are one way of
communicating extra information to a handler, but should we restrict
THROWs to not being able to pass any information from the place where
the error is detected to the place where the handler may make use of it?
...
>
>> The specification for THROW is too restrictive to allow for a
>> throw-code-dependent argument list on the stack. It may be possible to
>> define an alternate version of THROW which permits adding a specified
>> number of additional parameters for a given throw code, e.g.
>>
>> ... MAX_SLURP > or
>> if slurp_fid @ 1 1 VTHROW ...
>>
>> where VTHROW now adjusts the stack pointer to permit one additional
>> parameter on the stack, which is the file id to be used by the error
>> handler, for closing the file. If, for this error, we also want to
>> communicate the actual file size to the error handler, assume it was
>> locally saved in a 2VARIABLE fsize, and then the throw may be coded as
>>
>> ... MAX_SLURP > or
>> if fsize 2@ slurp_fid @ 3 1 VTHROW ...
>>
>> Note that 1 is the throw code for VTHROW and 3 is the number of
>> arguments on the stack, beyond the original stack depth.
>
> If I ever need to go there, the idea of passing additional data
> through a variable-stack-effect VTHROW looks in line with stuff like
> SET-ORDER, but I hope I never have to go there. And given that I have
> not needed to in the last 30 years, I think my chances are good.
>
A simpler solution might be to define +THROW which communicates two
cells to the handler ( ... -- ... x n ), where n is the throw code and x
can be a number, a structure address, or an object address. In order for
this to work with the existing CATCH, we would have to define a range
for extended throw codes so that the handler knows when x is available
on the stack. Words which can throw such extended information must
document what x is and whether or not, for the case of structures or
objects, the memory associated with x needs to be freed by the handler.
I'm not sure the argument that you haven't needed this in the last 30
years is a good argument to make. The lack of a capability tends to
influence one's style of programming. When the capability is present, it
may lead to a different style. Nevertheless, justification for +THROW
and an extended throw code range will need concrete examples. My first
example is the SLURP-FILE example.
--
Krishna