[Cuis-dev] Exception handler blocks with non-local returns

Phil B pbpublist at gmail.com
Mon Nov 4 08:27:02 PST 2019


Andres,

On Sun, Nov 3, 2019 at 6:36 PM Andres Valloud via Cuis-dev <
cuis-dev at lists.cuis.st> wrote:

> Phil,
>
> > Agreed and I think that's what I latched on to when you first raised the
> > issue.  This *could* cause some ugly problems down the road.  Just
> > because it doesn't currently doesn't mean it's not an issue as we might
> > not actually see a problem until some useful enhancement down the road
> > starts breaking things.
>
> When I had to debug exception frameworks, the difficulty in verifying
> any changes were ok increased with heterogeneous usage, that's for sure.
>
> Are we writing code to help others help us, or just to please ourselves?
>

Both... what would be the point of contributing if we didn't like the
result ourselves?


>   What are the long term consequences of those two approaches?
>

That's why I'm arguing for the functionality.  Eliminating the
functionality that is provided by '^' has short and long term consequences
that I think are problematic.


>
> > I'm with you as far as '^' usage in an exception handler block goes.  I
> > think where we have differing opinions is that I'm saying 'yes, let's
> > eliminate ^ from exception handler blocks'
>
> I'd be ok with this, especially if the way the code gets reorganized
> makes changing the code further even easier next time.  It's also a nice
> exercise in awareness to see how horribly difficult it can be to change
> or even understand truly heterogeneous code.
>
> > but at the same time saying
> > we should provide an explicit facility to allow for non-local returns
> > (i.e. #methodReturn or whatever.)  This accomplishes three things:
> >
> > 1) Avoids potential issues down the road and/or being in the
> > uncomfortable position of not being able to implement a feature because
> > it will break code that could/should have been legitimately considered
> > broken before.
> > 2) Provides a mechanism with equivalent functionality that we know will
> > *always* be correctly handled by the exception framework.
> > 3) Makes the intent explicit (i.e. 'yes, I really did mean to do a
> > non-local return in this exception handler')
>
> I can see that argument for the cases that remain after doing the
> cleanup you suggest.  Before I agree to this, though, I want to see a
> number of examples where there is really no better way to do things
> other than with non-local return (regardless of how it is actually
> expressed in code).
>

I can provide code for one example and talk through a couple others:

1) In OMeta, there are a few places where exception handlers capture rule
failures.  In one case (
https://github.com/pbella/OMeta-Cuis/commit/4884384de762ca8508b35a8952054f2333b40f26#diff-51b2428bf35caf95ff9fd8b0b88d37d8
- see line 677 of OMeta2Extensions.pck.st) the non-local return is
pointless: it has nothing to do with handling the exception so it makes
perfect sense to remove it since we would be returning that value had the
exception not occurred.  However in
https://github.com/pbella/OMeta-Cuis/blob/4884384de762ca8508b35a8952054f2333b40f26/OMeta2Preload.st#L544
and
https://github.com/pbella/OMeta-Cuis/blob/4884384de762ca8508b35a8952054f2333b40f26/OMeta2Preload.st#L606
the non-local return is related to handling the exception (i.e. there is
nothing more we can do in the current method, return to sender) so it seems
to make sense to keep the functionality in the handler.  Juan showed that
this can be worked around by effectively creating an 'outer' handler around
the handler block which performs the non-local return and it does work.
But how is this an improvement?  The non-local return is still being done,
just not from within the exception handler where it really belongs.  So now
the exception framework doesn't have a full picture of what's going on and
the code is a bit less straightforward.

2) Networking code.  Sometimes you will have a method whose job it is to
send/receive some data which makes a lot of implicit assumptions which can
be invalidated at a moment's notice.  Connections (esp. http) have a
tendency to just disappear which is to be expected and normal.  When that
happens, there's often no point in either trying to continue in the current
method (you can't do anything to 'fix' it... you no longer have a
connection to send to/receive from) or raise an exception up the call stack
(the sender can't do anything either): just stop what you were doing and
return to the sender whatever the most appropriate return value is. (i.e.
the failure should terminate the send/receive operation and the sender can
continue but sometimes doesn't need or want to know that it failed)

For example, I have a periodic task which involves ~10k http requests.  A
couple hundred of those will fail each run for various reasons: network
errors, server down, server errors, page errors etc.  There are exception
handlers at different levels of connection/request handling that decide if
the error is something that needs to be specifically handled / retried or
if terminating the request (or perhaps it was terminated on us) and
returning some specific value to the sender is the appropriate result.
These are almost always non-local returns since we're failing right in the
middle of processing a request which is no longer valid.

3) FFI code and dealing with the 'outside world' more generally.  Similar
situation as 2: something fails either in an expected place or in an
expected way.  The current method can't continue but the sender can.
Examples: a non-critical log file or database can't be found or has a
problem... (maybe) log something to transcript and return from the method
in question.  A backup file can't be copied to a secondary backup
location... we probably aren't connected to the network so ignore it and
try again the next time we get called.  I have several of these: fatal to
the current method but the sender doesn't care.

The thing all of these examples have in common is that they are all
essentially resumable, fatal exceptions... that need to resume somewhere
else.  Can you rewrite the methods in question to not perform a non-local
return in the exception handler?  Sure, but what does that get you?  Some
combination of pretending what happened didn't happen and/or that you
resolved the issue when you didn't but instead had the exception handler
spill outside the handler block.  So now you can say 'See?  No non-local
returns!' but all you've done is shifted them out of view of the exception
framework.  So we've gone from  bypassing the exception mechanism to lying
to it.


>
> With these types of things, it's really important to keep options open
> and always verify one's assumptions with actual evidence along the way.
>
> Here's an advantage of your suggestion I didn't see listed above: doing
> that would *define* what it means to do a non-local return in an
> exception handler block, and thus backfill a hole in section 5.5.2.1.
>

Yep.


>
> > I'm even in favor of saying 'from a best practices standpoint, we
> > recommend trying to avoid using #methodReturn' if we wanted to... just
> > provide it as an option.
>
> Yeah, I can see this in general.  I'd even phrase it as "from a best
> practices standpoint, we recommend avoiding non-local returns in
> exception handler blocks, but if you choose to do that then using the
> #methodReturn: method provided for that effect helps the maintainers of
> the exception framework keep things working as intended because said
> method *defines* what it means to do a non-local return in this context".
>

I'm fine with that.


>
> But let's wait until we get there, and especially after we get a feel
> for the most difficult cleanup cases.  There's no need to put that down
> in stone right now.
>

C'mon folks, the train is at the station and I'm on board.  Let's get
moving... ;-)


>
> > If we did that, then I have no problem in
> > flagging '^' in an exception handler as an error by default.  (Though we
> > would might still want to keep a preference option to not have it be an
> > error to help people porting code.)  It's taking away '^' without
> > providing an alternate way to do it, especially when it's so trivial to
> > do and logically consistent with the other functionality provided by the
> > framework, that I have a problem with.
>
> Agreed, one should think about the users of the system.
>
> > Let's use the Blue Book / ANSI
> > as a starting point, not the destination.
>
> Agreed as well --- no need to get dogmatic over a document that is 20
> years old, we can all think independently.
>
> So, action items... how about we go after the users of non-local returns
> in exception handler blocks and start cleaning them up piece meal?  We
> could start with that, see what remains after we deal with all the easy
> ones, and plan our next move.
>

I've already started in my code as you've sold me on it being something to
avoid when not needed.  I've been holding off going beyond that as I don't
think we've reached agreement as to the best course of action yet.  (i.e.
even if you and I agree, I believe Hernan still has a problem with the
whole concept)


> Andres.
> --
> Cuis-dev mailing list
> Cuis-dev at lists.cuis.st
> https://lists.cuis.st/mailman/listinfo/cuis-dev


Thanks,
Phil
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.cuis.st/mailman/archives/cuis-dev/attachments/20191104/18d59b27/attachment-0001.htm>


More information about the Cuis-dev mailing list