Hi. > > > > It is not because it doesn't convey any non-obvious information. > > How is this > > --- > > try { > > f.value(x); > > } catch (FunctionEvaluationException e) { > > console.warn(e); > > } > > --- > > more informative than this > > --- > > try { > > f.value(x); > > } catch (MathRuntimeException e) { > > console.warn(e); > > } > > --- > > ? [I mean, you "try" to call a method that will _evaluate_ the function, so > > that, when you "catch" something, it's, quite obviously, because the > > evaluation failed.] > > In this trivial case, yes, it is obvious, but it is a contrived example.
Making up an example with more lines only makes more difficult to see the obviousness, but it's still there ;-) (cf. below). Here I was showing that a *user* doesn't get anymore information from CM if "FunctionEvaluationException" were defined in CM (as opposed to being defined in his own application). > In real life applications, you will almost never have a catch just at > the same level of the call and you will not have a single line that can > fail. In real code, you have several layers of functions, even several > layers of libraries, and in a try/catch clause the try contains lots of > different lines, each of which being able to throw some exceptions. > > What we say is that classical use cases can be like this: > > > double myOwnAlgorithmWhichCallsSolverAtSomeDeepLevel(double a, > double b) > throws FunctionEvaluationException { > UnivariateRealFunction f = new UnivariateRealFunction() { > double value(double x) { > if (complexConditionIsNotMet(x)) { > throw new FunctionEvaluationException(x); > } > return x + 1.0; > } > } > > return solver.solve(maxEval, f, a, b); > > } > > > > void topLevelFunction() { > try { > > // numerous lines which use [math] and can fail > a = ...; > b = ...; > > // small part involving UnivariateRealFunction > result = myOwnAlgorithmWhichCallsSolverAtSomeDeepLevel(a, b); > > // still numerous lines which use [math] and can also fail > > } catch (FunctionEvaluationException fee) { > // here we known our complex condition was not met at some point > // so we need to adjust a and b > } catch (MathRuntimeException mre) { > // some OTHER unknown error occurred, we don't know which one (yet) > } > } So? If you remember, this is exactly what we talked about when we met in person. "myOwnAlgorithmWhichCallsSolverAtSomeDeepLevel" is defined in your application, thus *you* decide which exception it is going to throw (in this case, it is "FunctionEvaluationException" which, in my view, is defined somewhere in your application code). At "topLevelFunction", you *know* which exception to "catch" and you do it just as your example shows. I have nothing to say against this code. It is perfectly fine. Just, you cannot deduce from it that "FunctionEvaluationException" must exist in CM. As I had said at the time, you are free to select whichever abstraction you like for representing the failures of your own application-level code but they should not pollute the CM code. Let other users devise their own preferred abstractions. > In this exemple, I put only two levels, in many application ^^^^^^ [Part of the message is missing here.] > > > > Following your rationale, one would have to create one exception for each > > possible action (method). You have an API that would look like > > > > * "value" can raise an "EvaluationException" > > * "interpolate" can raise an "InterpolationException" > > * "solve" can raise a "SolveException" > > * "integrate" can raise an "IntegrationException" > > * "optimize" can raise an "OptimizationEception" > > etc, etc. > > No. We need very few user exceptions. Up to now, we had only two of > them: FunctionEvaluationException and DerivativeException. We considered > it would be sufficient to have one MathUserException only. I agree that we don't need those exceptions. I think that we need none that are meant as user-defined exceptions. Why do you think that "value" is exceptional (no pun intended) in this respect? > > > > If you want to talk in terms of boundaries, I think that these abstractions > > are on the other side of the CM boundary, i.e. they are useful to users of > > CM within their own code. > > On this issue, we have been in disagreement for a long time; I'm pretty sure > > that this is because both Luc and you are heavy users of CM and you cannot > > separate your role of developer of CM from the role of developer of > > applications-that-use-CM. > > It may be so, but at least for myself I really try to think about API > from user side and not from developer side. In fact, in our discussion I > think I have always preferred a solution that was simple for users > rather than simple for us, [math] developers. This very discussion is an > example of that: I prefer we provide a MathUserException for user > convenience, even if it implies having to document an exception we > should NEVER thorw by ourselves (hence I really think Phil has the right > solution when he suggest we change our current use of MathUserException > by another exception in same hierarchy). This definitely proves that you don't understand my point(s). Please don't consider that I try to make my life easier by slashing one or two exception classes! It would be easier if I were not trying to convince you to clean up what seems to be CM inconsistencies. The thing is a question of design principle. The library is designed to perform some tasks and warn of the precise problems that it encounters while doing so. As an example, you find that "MathUserException" is convenient for you (as a user). Would it be less convenient it were defined in you own collection of exceptions? Why pushing it into CM? The core issue is that there exists a class that doesn't perform any tasks nor provide any specific information on a problem that occurs within CM code. Clean-up means removing useless code. This is better for users and developers alike. > > > > In your application, you could have some code like > > --- > > import org.apache.commons.math.analysis.solvers.UnivariateRealSolver; > > import org.apache.commons.math.analysis.solvers.BrentSolver; > > import org.apache.commons.math.analysis.UnivariateRealSolver; > > import org.apache.commons.math.exception.NoBracketException; > > import org.apache.commons.math.exception.TooManyEvaluationsException; > > import org.apache.commons.math.exception.MathRuntimeException; > > import com.psteitz.nice.app.ApplicationFunction; > > import com.psteitz.nice.app.exception.FunctionEvaluationException; > > import com.psteitz.nice.app.exception.SolverException; > > > > UnivariateRealSolver solver = new BrentSolver(1e-4, 1e-6); > > UnivariateRealFunction f = new ApplicationFunction(); > > try { > > solver.solve(20, f, 1, 3); > > } catch(NoBracketException e) { > > throw new SolverException("No bracketing"); > > } catch (TooManyEvaluationsException e) { > > throw new SolverException(new ConvergencException(e.getMax()); > > } catch (FunctionEvaluationException e) { > > throw new SolverException("Evaluation failed"); > > } catch (MathRuntimeException e) { > > throw new SolverException("Undocumented CM failure: " + e); > > } catch (Exception e) { > > throw new SolverException("CM bug: " + e); > > } > > --- > > where > > "NoBracketException", > > "TooManyEvaluationsException", and > > "MathRuntimeException" > > are low-level exception classes defined in the low-level CM library > > (describing the exact problem as encoutered by the CM code), and > > "FunctionEvaluationException" and > > "SolverException" > > are appropriate abstractions for your application. > > > > The first two "catch" blocks are there because the CM library documents that > > those problems can arise from calling "solve". > > The third one is there because you (as application-developer) decided that > > "AplicationFunction" can raise such an exception (and this > > "FunctionEvaluationException" is not defined in CM; it is defined in > > relationship with the "AplicationFunction" that can raise it). > > The fourth is there as a security measure (for the application) in case > > "solve" did not behave according to its Javadoc. [The security holds if CM > > globally repects the policy that all exceptions are subclasses of > > "MathRuntimeException".] > > The last one protects the application from CM bugs. > > > > You have to let the application developer decide how low-level exceptions > > translate to concepts useful to the application at hand, not the other way > > around as it is impossible in all generality (because the low-level is > > lacking the "context"). It's a pity that you didn't comment on the above example. It shows exactly my viewpoint of how to best separate the responsibilities of user code and CM code. What you'd find problematic here could shed some light on our divergence. Oops, re-reading the example now, I notice that I forgot to mention "ConvergenceException" in the comments below the code: "ConvergenceException" is currently *the* exception that could arguably lie on both side of the boundary (i.e. defined either in CM or in user code). I agreed that it could well be the appropriate description on the problem in "ContinuedFraction" (i.e. that it carries more information that "NotFiniteNumberException"). However, it was still not clear how "ConvergenceException" would convey the underlying problem (the one that actually makes the CM code fail). Probably having "ConvergenceException" wrap the "NotFiniteNumberException" would the most complete representation of the failure. If so, "ConvergenceException" does certainly belong in CM. > > [...] Best, Gilles --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org