lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On 2018-09-18 23:24, Tim Hill wrote:

On Sep 17, 2018, at 12:36 AM, Sean Conner <sean@conman.org <mailto:sean@conman.org>> wrote:

1) it is handled by the routine itself
 a) no check is performed at all [5]
 b) the error condition is noticed but ignored

1c) The error condition is corrected by the routine itself.

For example, open a file at location A, fail, try location B, succeed.

Oooh, that reminds me of a thing that I tried a while ago... replace
`pcall` with `hopefully`!

[tl;dr: handling exceptions vs. expectations]



The try/catch (error/pcall, return nil,err / if, ...) approach is to
describe the error, throw away (part of) the stack, then try to recover.
There may be structured error descriptions (hierarchy/tags/...) that
might be "open" / extendable, but the expectation / intended result
isn't really encoded in a structured way, which means the error handling
happens in an essentially ad-hoc manner (you spell everything out all
the time...) and is "closed" / cannot be extended.

The approach I used was to have (potentially a hierarchy of) structured
_expectations_ that have a default action and an associated list of
failure handlers (which – if reachable – is "open" / extensible). From
the user code, you call `hopefully( desc, params... )` (could also be
hidden inside some constructor) and if the action fails, that tries the
handlers in order (with the stack still intact – so this is a normal
function, no language support needed etc.). If some handler manages
to fix the problem, your code just gets the result – it doesn't even see
that there were some failures. If all handlers fail, you get a plain
exception – at which point you can generally just let it throw away the
stack (maybe after doing some cleanup), because you know there's nothing
you can do to fix it.

So this doesn't fully replace normal error handling... but it reduces
the amount. In the best case, your code looks like a "happy path"
program, with dangerous places marked `hopefully(...)`. Where you need
cleanup, that can be split fairly easily into a main/cleaning function
combo, with a generic helper function that does the pcall / ok,... split
/ if dance. (Or maybe __gc is good enough, or maybe your pcall wrapper
triggers a garbage collection to make __gc good enough, or maybe it
keeps a coarse "shadow stack" that tracks pcall levels and things to
explicitly free, or...)



Ok, so far, so boring... that may be quite nice but it's not really
worth the extra effort... right?

So... let's say you're opening files and you have a fallback directory.
No problem, just wrap the file opening stuff in a function and use that
in your code. But libraries won't... ugh. So you pre-process paths all
over the place (or write a wrapper around parts of the libraries...) Or
(if those libraries were doing the structured expectation thing) you'd
add a handler for the "readable file from path" expectation that tries
the fallback path, and now everything magically works.

Or (more generally), if you happen to know that there's a user sitting
around, why not ask them what to do? You can add a handler that asks
them whether to retry / use a different name / abort. (GUI or CLI? And
which flavor? Doesn't matter – it's your handler, do what works for
you.) If expectations are structured in a hierarchy, you could even set
a top-level fallback that may decide whether to pop up a dialog or do
nothing and let other handlers try to fix it.

If there's stuff that may temporarily fail and your code is structured
as coroutines, add a handler that yields and eventually (on resume)
retries? (A library writer can do an immediate retry, but they can't (or
shouldn't) delay (single-threaded code!) for longer, can't really assume
coroutines, can't... do anything to fix it, really. But you know your
environment, know stuff that the library writer can't assume – and that
may add a bunch of options for dealing with problems... Recall how I
said that normal exceptions are "closed" and this is "open"? See the
difference?)



Does this approach have a common name? Has anyone else tried this /
knows a place where this was used? Particularly on larger code bases:
Does it keep working... or will it go up in flames? (I haven't seen this
elsewhere yet, tried this a bunch of times on smaller experiments... It
worked fine, but it looks like getting the balance on the structure of
expectations right (over-/"under-engineering") will be tricky for larger
stuff.)

-- nobody