On 12/22/15 1:54 AM, Luc Maisonobe wrote: > Hi all, > > Le 21/12/2015 20:41, Phil Steitz a écrit : >> On 12/21/15 11:16 AM, Gilles wrote: >>> On Mon, 21 Dec 2015 10:01:33 -0700, Phil Steitz wrote: >>>> On 12/21/15 8:21 AM, Gilles wrote: >>>>> On Mon, 21 Dec 2015 06:06:16 -0700, Phil Steitz wrote: >>>>>> On 12/20/15 8:41 PM, Gilles wrote: >>>>>>> On Sat, 19 Dec 2015 11:35:26 -0700, Phil Steitz wrote: >>>>>>>> On 12/19/15 9:02 AM, Gilles wrote: >>>>>>>>> Hi. >>>>>>>>> >>>>>>>>> While experimenting on >>>>>>>>> https://issues.apache.org/jira/browse/MATH-1300 >>>>>>>>> I created a new >>>>>>>>> JDKRandomGeneratorTest >>>>>>>>> that inherits from >>>>>>>>> RandomGeneratorAbstractTest >>>>>>>>> similarly to the classes for testing all the other RNG >>>>>>>>> implemented >>>>>>>>> in CM. >>>>>>>>> >>>>>>>>> The following tests (implemented in the base class) failed: >>>>>>>>> 1. >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> testNextIntNeg(org.apache.commons.math4.random.JDKRandomGeneratorTest) >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> Time elapsed: 0.001 sec <<< ERROR! >>>>>>>>> java.lang.Exception: Unexpected exception, >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> expected<org.apache.commons.math4.exception.MathIllegalArgumentException> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> but was<java.lang.IllegalArgumentException> >>>>>>>>> 2. >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> testNextIntIAE2(org.apache.commons.math4.random.JDKRandomGeneratorTest) >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> Time elapsed: 0.015 sec <<< ERROR! >>>>>>>>> java.lang.IllegalArgumentException: bound must be positive >>>>>>>>> >>>>>>>>> This is caused by try/catch clauses that expect a >>>>>>>>> "MathIllegalArgumentException" >>>>>>>>> but "JDKRandomGenerator" extends "java.util.Random" that for >>>>>>>>> those >>>>>>>>> methods throws >>>>>>>>> "IllegalArgumentException". >>>>>>>>> >>>>>>>>> What to do? >>>>>>>> I would change the test to expect IllegalArgumentException. >>>>>>>> Most >>>>>>>> [math] generators actually throw NotStrictlyPositiveException >>>>>>>> here, >>>>>>>> which extends MIAE, which extends IAE, so this should work. >>>>>>> It turns out that, in the master branch, the hierarchy is >>>>>>> >>>>>>> RuntimeException >>>>>>> | >>>>>>> MathRuntimeException >>>>>>> | >>>>>>> MathIllegalArgumentException >>>>>>> >>>>>>> as per >>>>>>> https://issues.apache.org/jira/browse/MATH-853 >>>>>>> >>>>>>> [And the Javadoc and "throws" clauses are not yet consistent with >>>>>>> this >>>>>>> in all the code base (e.g. the "RandomGenerator" interface).] >>>>>>> >>>>>>> So, in 4.0, "JDKRandomGenerator" should probably not inherit from >>>>>>> "java.util.Random" but delegate to it, trap standard exceptions >>>>>>> raised, >>>>>>> and rethrow CM ones. >>>>>> I guess that is the only way out if we are going to stick with the >>>>>> MATH-853 setup. This example illustrates a drawback identified in >>>>>> the thread mentioned in the ticket. I would personally be happy >>>>>> reverting master back to the 3.x setup. >>>>> We can't throw (!) the many discussions that led to MATH-853 by >>>>> just >>>>> reverting on the basis of the current issue. >>>>> >>>>> Perhaps that the multiple hierarchies are better, maybe not. >>>>> But we have to figure out arguments that are more convincing than >>>>> nostalgia. >>>>> >>>>> For instance, IMO, we have contradicting wishes: >>>>> * Have CM exception inherits from the JDK ones with the same >>>>> semantics. >>>>> * Have all CM exceptions provide the same additional functionality >>>>> (the >>>>> "ExceptionContext"). >>>> In 3.x, MIAE extends IAE and we have all the context stuff working. >>>> >>>> I guess you are concerned about the "duplicated code" in the >>>> multiple roots extending the standard exceptions. Here is the full >>>> extent of it: >>>> >>>> /** >>>> * @param pattern Message pattern explaining the cause of the >>>> error. >>>> * @param args Arguments. >>>> */ >>>> public MathIllegalArgumentException(Localizable pattern, >>>> Object ... args) { >>>> context = new ExceptionContext(this); >>>> context.addMessage(pattern, args); >>>> } >>>> >>>> /** {@inheritDoc} */ >>>> public ExceptionContext getContext() { >>>> return context; >>>> } >>>> >>>> /** {@inheritDoc} */ >>>> @Override >>>> public String getMessage() { >>>> return context.getMessage(); >>>> } >>>> >>>> /** {@inheritDoc} */ >>>> @Override >>>> public String getLocalizedMessage() { >>>> return context.getLocalizedMessage(); >>>> } >>>> >>>> I see that as NOT_A_PROBLEM. In 3.x, we have 6 classes that need >>>> this little bit of similar code. That is after 10 years of >>>> development. I doubt we will have more than a few more needed at >>>> the top level. >>> It's not a problem of how many lines of code must be duplicated. >>> >>> The problem is the duplication. >>> Duplication is not a best practice AFAIK. >> I agree that duplication is to be avoided; but I think it does make >> a difference how much / how complex the code is. The duplicated >> code in this case is trivial. >>>>> The first looks nice from a user POV (as in "I know this already"). >>>>> The second is an internal requirement to avoid code duplication. >>>>> >>>>> IMO, it always comes back to those opposite views we have >>>>> concerning the >>>>> place where human-readable messages should be generated: my view >>>>> is that >>>>> if a low-level library message bubbles up to the console, then >>>>> it's not >>>>> our fault, but the application programmer's. >>>> Meaningful exception messages are a best practice in Java. They are >>>> a very useful aid in debugging and production support. >>>>> Please assume that for a moment; then CM could have its algorithms >>>>> throw >>>>> some internal subclass of "MathRuntimeException" (whose type is >>>>> known to >>>>> the caller) and the caller can decide whether he must trap it in >>>>> order >>>>> to rethrow a standard type, or his own type (tailored to the >>>>> context which >>>>> he knows but CM doesn't). >>>>> >>>>> In that scenario, it is much easier for the caller when the >>>>> low-level >>>>> library's exceptions hierarchy is unique (especially when he uses >>>>> several >>>>> libraries). >>>>> And it's also easier for us because we can avoid code duplication. >>>> Easiest is if the library follows the Java best practice, which is >>>> to favor standard exceptions. >>> We do not favour standard exceptions because we _have_ to define our >>> own. >>> And we have done this for various "good" reasons in line with other >>> best practices, subject to internal requirements. >> We can have it both ways if we define our own exceptions to extend >> the standard exceptions where that makes sense, which is the best >> practice. Having MIAE extend IAE is good design, IMO. What we are >> throwing on bad arguments is IAE. Callers can catch that. If they >> want to dig in deeper and differentiate their handlers based on >> which of our IAE subclasses is thrown, they can do that. That is >> how exception hierarchies are supposed to work. The "favor standard >> exceptions" principle is there so people don't go and define "new" >> exceptions that are just renamed versions of the standard >> exceptions. That is precisely what MIAE does in V4. The base MIAE >> *is* IAE, just renamed, disconnecting all of the exceptions derived >> from it from their natural root, which is IAE. > One of the point in having exceptions that extends our own root > exception is that users at higher level can catch this top level. > Currently, we don't even advertise properly what we throw. We even > miss to forward upward some exceptions thrown at low level in the > javadoc/signature of out upper level methods.
This is bad. We should keep chipping away at fixing it. > > So user may currently not know, only reading the javadoc/signature > of one of our implementation that they may get a MIAE or something > else. If we were using a single root, they would at least be able > to do a catch (MathRootException) that would prevent a runtime > exception to bubble up too far. Not much different from "catch Exception" except I guess it will catch only what is thrown by "us." > Currently, defensive programming > to protect against this failure is to catch all of > MathArithmeticException, MathIllegalArgumentException, > MathIllegalNumberException, MathIllegalStateException, > MathUnsupportedOperationException, and MathRuntimeException. > > In a perfect world, we would be able to extend a regular IAE while > implementing a MathRootException, but Throwable in Java is a class, > not an interface. Too bad. Sorry for reopening this can of worms. I see I am in the minority. Apologies for the noise. Phil > > best regards, > Luc > >>>> Then these can be caught without >>>> rummaging through library-specific javadoc to figure out what >>>> unchecked exception means what. This is especially true for >>>> unchecked exceptions. >>> A standard exception does not mean anything specific. >> It does. That's the point. IAE means you have provided bad >> arguments, ISE means you have encountered bad state. >> ArithmeticException means an exceptional condition has been >> encountered performing an arithmetic operation. We should, where >> natural, derive from these. >>> So why bother with the "implementation detail"? >>> >>> try { >>> /** any code that deep down may or may not call CM */ >>> } catch (RuntimeException e) { >>> // Deal with it or rethrow. >>> } >>> >>> will work, always. User does not need to know anything to print >>> and read the meaningful message, only JDK's "RuntimeException" here. >>> >>> The problem is _not_ there. >> Not sure I follow what you mean. >>>>> You never convinced me that you were aware of this type of >>>>> scenario and >>>>> why, somehow, it was trumped by a nicely formatted message on the >>>>> console. >>>>> The more so that a single hierarchy does not prevent the latter: >>>>> at the point where the message is printed, the exception type is >>>>> useless. >>>> I think we can have it both ways. The 3.x setup allows us to extend >>>> standard exceptions *and* provide good exception messages. >>> The question is: Why extending standard exceptions? >>> We came to another answer in MATH-853. >>> >>> So the problem cannot be solved as if it did not exist. >> I guess I am seeing more consequences now of completely proprietary >> exceptions. It bothered me then. It bothers me more now as I >> think about the consequences for client code. >> >>>>> To summarize, if the top-level message matters most, then I'd be >>>>> -1 to revert >>>>> (because that's an orthogonal issue); if having standard types >>>>> matters most, >>>>> I'd wait for use-cases that show how these types are used in the >>>>> caller's >>>>> code before trying to figure out how to satisfy them (while taking >>>>> our >>>>> internal requirements in to account too). >>>> Well, we have a nice example in front of us in our own test case >>>> here. But the bigger deal is the pain for every user who when >>>> migrating from 3.x to 4 has to stop catching standard IAE if they >>>> did this and replace it with MIAE. Note that the compiler will be >>>> no help there - failure to "comply" will cause the client app to "go >>>> boom." >>>> >>> All this has been said again and again. >>> >>> I'd like that we do not take our own assumptions for granted >>> (be they the scenario which I described, or that compatibility >>> is the ultimate goal, even if the design is crap) but instead >>> that we could talk about what a live project CM can be, looking >>> to what can be done in possibly novel ways. >>> >>> A new major version is an opportunity to legally break >>> compatibility in order to try new things. >>> Instead we have full-time discussions to avoid changing anything. >> Not a fair statement. I want to make sure that the changes we make >> are positive. >> >> Phil >>> Users are never forced to follow development. >>> >>> Gilles >>> >>> >>>> Phil >>>>> In conclusion, let's not jump to conclusions. ;-) >>>>> >>>>> >>>>> Gilles >>> >>> --------------------------------------------------------------------- >>> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org >>> For additional commands, e-mail: dev-h...@commons.apache.org >>> >>> >> >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org >> For additional commands, e-mail: dev-h...@commons.apache.org >> >> > > --------------------------------------------------------------------- > To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org > For additional commands, e-mail: dev-h...@commons.apache.org > > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org