On Sun, Jan 30, 2011 at 10:20:33AM -0500, Phil Steitz wrote: > > On Jan 28, 2011, at 11:31 PM, Gilles Sadowski wrote: > > > Hello. > > > >>>>>>> OK. But now that we have detected an "aroma" around unilaterally > >>>>>>> making UnivariateRealFunction throw Math*User*Exception, I wonder if > >>>>>>> there is a way to introduce an unchecked parent that gets us out of > >>>>>>> this. We may want to reserve the right to do this in 3.0, so the "head > >>>>>>> start" in 2.2 might be a head start to nowhere, unless we find a way > >>>>>>> to fix it in 2.2 (or you convince us that the current setup is an OK > >>>>>>> long-term solution). > >>>>>> > >>>>>> I don't understand what you mean. > >>>>>> "MathUserException" is unchecked so it has no consequence that it is > >>>>>> in a > >>>>>> "throws" clause. > >>>>>> Or do you want to not remove FunctionEvaluationException from the > >>>>>> "throws" > >>>>>> clause (because it is not a backward-compatible change)? > >>>>>> > >>>>> The "aroma" I was referring to is that MathUserException is not, > >>>>> strictly speaking a suitable replacement for > >>>>> FunctionEvaluationException. The intent as described in the javadoc > >>>>> for MathUserException is that it allows exceptions in user-defined > >>>>> functions to be propagated through [math] API layers (an excellent > >>>>> idea, IMO). > >>> > >>> I somewhat agreed on this point, because it doesn't hurt, although, as > >>> said > >>> earlier, I really doubt that we can set a standard. [Anyway IMO it's fine > >>> that users create whatever exception they like.] > >>> > >>>>> The problem is that FunctionEvaluationException is > >>>>> broader - it could apply to non-user-defined functions, as in the > >>>>> interpolation code that Luc pointed out. > >>> > >>> I mentioned that the "interpolate" method creates an object that > >>> implements > >>> the "UnivariateRealFunction" interface. > >>> Unless I'm missing something, the "problem" you mention does not exist. > >>> The > >>> actual problem was the very _existence_ of "FunctionEvaluationException": > >>> A > >>> class that was almost never actually instantiated within CM (and in the > >>> places where it was, it was the wrong thing to do). [And the fact that is > >>> was a chacked exception made things worse: try/catch all the way up for > >>> something that never happens! That's why I argued that it be removed.] > >> > >> I understand your point, > > > > I'm not so sure. Maybe I don't explain clearly. > > > >> but I disagree with it. We are back to a > >> basic principle of API design that we need to settle. My view is that > >> FunctionEvaluationException absolutely makes sense at the API boundary > >> of UnivariateRealFunction#value. It is the right abstraction at that > >> level - it says that an exception occurred evaluating a function. > > > > 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.] > > > > 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. > > > > 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. > > > This is sort of a core principle of how OSS in general and Commons in > particular works - developers "scratch itches" based on their practical needs > and the software benefits tremendously from that. What results is software > that meets the practical needs of users. When developing reusable components > - especially OSS components - we absolutely must put the perspective of the > library user first. This is not something that we can argue about. It is how > we work @apache and in Commons.
As I've stated in the previous post, I don't argue from the stand-point of a supposedly lazy developer. When I'm stressing that I think that we should think more about the separation of concerns between user code and library code, it is with the intent of improving the design of CM. Because good design makes the code easier to maintain, which increases its lifetime, which ultimately increases users confidence. Conversely, bad design will ulitmately kill your project, however featureful. > Our exceptions design really needs to meet the needs of both [math] > developers and [math] users. A well-structured hierarchy that expresses both > high-level (e.g., FunctionEvaluationException, ConvergenceException) and > low-level (e.g. NonSymmetricMatrixException) will make the lives of both > users and [math] developers easier. Other Commons components and successful > libraries succeed in meeting the needs of both end users and internal > developers and we need to do the same. Some "comparative shopping" might > help us here and maybe cajoling some other Commons developers with experience > maintaining libraries into jumping in here might help us. One thing we > always need to keep in mind is that the public API is our contract to our > users - everything we expose we are telling them they can use and depend on. I must admit that I have no idea of the difference. What are the needs of the developers and those of the users? What I see is that when a user needs a feature, he fills a "Wish" request; then some developer implements the feature. IMO, the exceptions issue is a bad example for interrogating users as to what they would like because (I think that) exceptions are linked to the code base (i.e. they are defined to report partiular events that the code base can encounter. For exceptions in particular the top-down approach ("What would be nice to have as exceptions?") seems doomed to fail; instead I prefer to focus on a feature (an algorithm), recognize what can go wrong, and create the corresponding exception that will report just that. Note that it does not preclude to come up with a more comprehensive (high-level if you wish) set of exceptions but I recommend to not do that without discussing several real use-cases from users who are not developers. I'm all for requesting expert advice on library design and/or examining how reputable libraries are designed. > I don't follow the example you presented. I was more thinking about > situations where as an external user or internal developer I might pass a > function an argument outside of its domain (and I might know this in > advance). This is a basic situation, similar to failed convergence. One > could encounter it, for example, when computing the value of the objective > function for the start value passed to a solver. If the start value is > outside the domain of the function, then the UnivariateRealFunction should > throw a FunctionEvaluationException. We can discuss further how > FunctionEvaluationException might be refined, but ArgumentOutsideDomain makes > sense in this case. Note that this is *much better* than something like > "NumberTooLarge" or other disjointed low-level exception from the standpoint > of the caller (the actor that *really counts* - whether this is internal > [math] code or user code). The code that passes the bad initial value could > well be able to recover by perturbing whatever algorithm it is using to > select the initial value. Evaluating a user-defined function can throw any user-defined exception. It doesn't have to be a "FunctionEvaluationException". Within the CM code, we have the "OutOfRangeException"; we defined it be cause we use it. Other users can decide that they will re-use it, but they can also decide to define their own exception because nothing in CM suits their needs. If so, a "FunctionEvaluationException" class will also not suit their needs because it is not a specific (it just says "Oops, something went wrong", but when an exception is thrown, you already know that!). Concrete example: In the code I'm developing for the project I work in, there is a (multivariate) function. The function represent some physical model and some combinations of the parameters can violate assumptions of the model. When an invalid combination that violate a specific assumption is detected, a specific exception (representing the violated assumption) is thrown. "FunctionEvaluationException" is quite unable to represent the concept represented by my own exceptions. Indeed, because the API is a contract, it is all the more important to think twice (or more) about it, and not just introduce things that seem to look like they could be useful to someone (maybe). Please don't jump on the conclusion that I'm saying that "FunctionEvaluationException" is not useful to Luc or to you. What I'm saying is that the fact that it is does not imply that it is OK (design-wise) to push it into CM. > You make a good point about us needing to be careful not to go overboard with > too many overly-specific exceptions. I agree strongly that introducing a new > exception for every algorithm or method in [math] is not what we want to do. > Similar to data structures (I think we have done a good job avoiding > introducing abstract entities until we really have need for them), we need to > be parsimonious and build the hierarchy based on concepts with wide > application. In my opinion, FunctionEvaluationException and > ConvergenceException are examples of these. I agree that things like > "InterpolationException" are not; though things like SingularMatrixException > are. The latter is an excellent example of a broadly useful exception like > FunctionEvaluationException. Just as I may know that it is possible in my > user or internal [math] code that I might pass an argument to a function that > is outside its domain or that my numerical implementation of the function > will choke on, I might know in other situations that a matrix > may become numerically singular. In each case a) being able to catch the > exception and b) seeing the meaningful exception in the stack trace are > useful to me (with either hat on). There is at least one use-case in CM for "ConvergenceException" (namely in "ContinuedFraction"). There is none (*within* CM) for "FunctionEvaluationException". It was indeed the "idea" that the concept of "UnivariateRealFunction" mandated the concept of "FunctionEvaluationException" (implemented as a checked exception) that created a (real) burden on application developers. It was so harmful that even code within CM was catching the "FunctionEvaluationException" and wrapping it inside a "RuntimeException" and rethrowing that one! I've been pushing for a thorough clean-up of the exceptions, but before it is finished and we can see how it goes with the bare minimum (that is, eliminating all checked exceptions and redundant messages), you want to re-instate the first (and most obvious one, IMO) that was eliminated. Regards, Gilles --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org