commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Luc Maisonobe <Luc.Maison...@free.fr>
Subject Re: [Math] Exception handling (again)
Date Sun, 23 Jan 2011 21:18:18 GMT
Le 23/01/2011 22:10, Phil Steitz a écrit :
> On Sun, Jan 23, 2011 at 2:45 PM, Luc Maisonobe <Luc.Maisonobe@free.fr> wrote:
>> Hi Phil,
>>
>> Le 23/01/2011 19:27, Phil Steitz a écrit :
>>> Thanks for restarting the discussion of this topic, Gilles.
>>>
>>> Here is a first attempt at some principles for exceptions management
>>> in [math], followed by an illustration using the ConvergenceException
>>> case discussed in MATH-487.  The principles are no doubt incomplete
>>> and some may be wrong and/or not acceptable to the community.  Lets
>>> try to find consensus on a simple set of principles that we can agree
>>> on and use as a guide in the 3.0 refactoring and new development.  I
>>> will update the Developers Guide with what we eventually agree to.
>>>
>>> 0) Exceptions generated by [math] are all unchecked.
>>>
>>> 1) All public methods advertise all exceptions that they can generate.
>>>
>>> 2) All exceptions inherit from the base class, MathRuntimeException
>>>
>>> 3) Whenever possible, methods fully specify parameter preconditions
>>> required for successful activation.  When preconditions are violated,
>>> a MathIllegalArgumentException should be thrown.  Subclasses of
>>> MathIllegalArgumentException may be used to represent common parameter
>>> contract violations (for example, NoBracketingException).  Exception
>>> messages must contain sufficient information on parameter values to
>>> determine the exact precondition failure.
>>>
>>> 4) Exceptions generated by [math] make sense without knowing
>>> implementation details other than those stated in the public API.  For
>>> example, a NoBracketingException makes sense thrown by a solver that
>>> has an API precondition requiring that initial points bracket a root.
>>>  This exception does not make sense, however, thrown by an inverse
>>> cumulative probability estimator.
>>>
>>> Together, 3) and 4) imply
>>>
>>> 5) MathIllegalArgumentException should only be thrown in situations
>>> satisfying 3) - i.e., unless the preconditions can be exhaustively
>>> provided so that what arguments are "illegal" can be specified fully
>>> to the caller, different exceptions should be thrown on the event of
>>> failure.  For example, the exact domain of successful activation of a
>>> solver or quadrature method may be impossible to specify because of
>>> numerical properties of the method.  If a solver fails to find a root
>>> or a quadrature method fails to converge for a given set of
>>> parameters, *unless those parameters violate the advertised
>>> preconditions* it is not appropriate to throw
>>> MathIllegalArgumentException.  Domain-specific exceptions need to be
>>> defined for these cases.  For example, SingularMatrixException should
>>> not inherit from MathIllegalArgumentException unless it is only thrown
>>> in situations satisfying 3) .  All current uses of
>>> SingularMatrixException do satisfy 3), though the javadoc in some
>>> cases needs a little cleanup to make it clear how singularity violates
>>> the contract.  My HO is that it would be better for
>>> SingularMatrixException to not extend MathIllegalArgumentException,
>>> substituting instead either just MathRuntimeException or a linear
>>> domain-specific parent (shared by the other matrix exceptions).
>>>
>>> Now on to the ConvergenceException example.  Many of our algorithms
>>> amount to generating a sequence of values, hoping that the sequence
>>> converges (really "gets Cauchy enough") and returning an estimate of
>>> the limit of the sequence.   Thus far, convergence is always in an
>>> incomplete metric space and we usually have the extra-mathematical
>>> issue to deal with that one form of divergence is becoming NaN.
>>> Convergence failure happens when arguments are outside the
>>> effectiveness range of the algorithm.  It is often impossible to state
>>> precisely in preconditions exactly what the conditions are on the
>>> parameters that will lead to failed convergence.  This is especially
>>> true when the implementation code contains bugs :)
>>>
>>> Failed convergence manifests in three ways: a) maximum iterations
>>> exceeded without meeting the convergence criteria b) iterates diverge
>>> to an infinity an infinite result is not correct c) iterates "diverge
>>> to NaN" and NaN is not correct.  I suggest therefore, that we define:
>>>
>>> ConvergenceException extending MathIllegalStateException or even just
>>> MathRuntimeException;
>>>
>>> MaxIterationsExceededException extends ConvergenceException, including
>>> the max iterations as part of its state and message;
>>>
>>> IterationRangeException extends ConvergenceException, including the
>>> out of range value encountered and what element of the sequence
>>> attained the bad value (this allows values other than NaN or
>>> infinities to count as "divergence").
>>>
>>> We could also add InfiniteIterateException and NaNIterateException
>>> extending IterationRangeException for these cases.
>>>
>>> I have often wanted to know how far out in the sequence one of the
>>> values becomes infinite.  Capturing this information in the exception
>>> and including it in the message would be an improvement, IMO.  The
>>> fact that you *can* do this in the setup above is an illustration of
>>> why I like domain-specific exceptions.
>>
>> Wow! That is a thorough analysis and a constructive one.
>>
>> I fully agree with you. I'm not sure that IterationRangeException is
>> needed, but that is a minor point.
> 
> I am curious why you see IterationRangeException as not needed when
> MaxIterationsExceededException is, unless you think that the latter is
> also unnecessary.  These are really the two logical subclasses of
> ConvergenceException.  If we eliminate both of them, then we would
> need an enum or something else to indicate the nature of the failed
> convergence (iterates going out of range or max iterations exceeded).

Ah, OK I had something different in mind, my bad.

Luc

>>
>> Concerning point 1), there are two ways to advertise the exception.
>> Javadoc only, or Javadoc plus throws clause. I personnaly would prefer
>> Javadoc plus throws clause
> 
> +1 for both, with throws mandatory and javadoc also for all but
> trivial cases.  The conditions under which the exception may be thrown
> *must* be fully specified in any case.  If only mentioned in throws,
> these must be pretty simple.
> 
>> as it is simpler to make sure we didn't
>> forget anything (using the trick of temporarily changing the base class
>> for MathRuntimeException and look at compiler errors).
> 
> Now *that* is a good trick.  Thanks for point it out :)
> 
> Phil
>>
>> Big +1
>>
>> Thanks,
>> Luc
>>
>>>
>>> Phil
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
>>> For additional commands, e-mail: dev-help@commons.apache.org
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
>> For additional commands, e-mail: dev-help@commons.apache.org
>>
>>
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Mime
View raw message