|
From: Raymond T. <toy...@gm...> - 2018-05-30 15:57:33
|
>>>>> "Kris" == Kris Katterjohn <kat...@gm...> writes:
Kris> Hey everyone,
Kris> I'm working on a bug fix and ERRSET's behavior when it comes to multiple
Kris> values has come up. I'm proposing a possible change below. Either I
Kris> can make the change or I'll do something else for the bug fix.
Kris> The ERRSET variable and macro are used in quite a few places in Maxima.
Kris> When ERRSET is non-NIL, the ERRSET macro allows errors that occur during
Kris> the evaluation of its first argument to propagate. When ERRSET is NIL,
Kris> the ERRSET macro currently behaves like this:
Kris> (errset (+ 1 'doh)) => NIL
Kris> (errset (+ 1 2)) => (3)
Kris> (errset (values 1 2 3)) => (1)
Kris> (errset (values)) => (NIL)
Kris> That is, NIL is returned if an error occurs and a singleton list
Kris> containing the (primary!) return value from the evaluated form is
Kris> returned if no error occurs.
Kris> Notice that ERRSET and multiple values don't mix well. It's unclear
Kris> whether or not extra return values got silently dropped, and there is an
Kris> ambiguous case where we get a singleton list containing NIL if the form
Kris> really does return NIL or if it actually returns no values at all.
Kris> I'd like to extend ERRSET to handle multiple values by having it return
Kris> a list of all return values from the evaluated form in the non-error
Kris> case. So then we'd have
Kris> (errset (values 1 2 3)) => (1 2 3)
Kris> (errset (floor 4/3)) => (1 1/3)
Kris> and so on. This is consistent with the current behavior when there is a
Kris> single return value.
Kris> This can be easily accomplished with a couple of changes like this:
Kris> (defmacro errset (&rest l)
Kris> - `(handler-case (list ,(car l))
Kris> + `(handler-case (multiple-value-list ,(car l))
Kris> (error (e) (when errset (error e)))))
Kris> The only possible problem I see is that now we have a different
Kris> ambiguous case: we can get NIL back if an error occurs or if the form
Kris> returns no values at all:
Kris> (errset (error "uhoh")) => NIL
Kris> (errset (values)) => NIL
Kris> That's certainly not optimal.
Kris> The test suite runs fine with this ERRSET definition.
Kris> To avoid this ambiguous case I could add a special case for it:
Kris> (defmacro errset (&rest l)
Kris> - `(handler-case (list ,(car l))
Kris> + `(handler-case (or (multiple-value-list ,(car l)) (list nil))
Kris> (error (e) (when errset (error e)))))
Kris> Then
Kris> (errset (error "uhoh")) => NIL
Kris> (errset (values)) => (NIL)
Kris> (errset (values 1 2 3)) => (1 2 3)
Kris> That's kinda gross, but this way the new behavior is precisely like
Kris> the current behavior when there are one or zero return values, it works
Kris> in the desired way when there are two or more return values, and no case
Kris> can be confused with the error case. So really the only difference is
Kris> that we get all return values instead of just the primary one when there
Kris> are two or more values.
I think your approach is good.
An alternative approach, which is a lot more work, is to return two
values like cl::ignore-errors. If there are no errors, return the
(multiple) values of the form. If there is an error, return nil and a
second value that is the error condition. This means updating all the
call sites to handle this case, though, and that might not be
desirable.
--
Ray
|