Java: using an exception for a normal event



  • So, I have this class, a root finder class for finding the places where a certain function is zero. I actaully have several descendants of this abstract class, each one with a different algorithm, but a bunch of code in the common abstract ancestor.

    <font face="Courier New">package *******.rootfind;

    public abstract class RootFind {
      public double Epsilon=1e-6;
      public Method convmethod;
      public enum Method {
        Relative,
        Absolute,
        Adaptive
      };
      private RootFunction R;
      private double Yt;
      public boolean IsConverged(double Y) {
        return(ErrorAmount(Y)<Epsilon);
      }
      public double ErrorAmount(double Y) {
        switch(convmethod) {
          case Adaptive:
            //If Y0<10, use difference
            //else use relative difference
            if(Math.abs(Yt)<10) {
              return Math.abs(Y);
            } else {
              return Math.abs(Y)/Math.abs(Yt);
            }
          case Relative:
            return Math.abs(Y)/Math.abs(Yt);
          case Absolute:
            return Math.abs(Y);
          default:
            throw new IllegalArgumentException("This can't happen! Strange enumeration value found");
        }
      }
      protected abstract double Step() throws <font color="#ff0000">LuckOutException</font>;
      protected abstract void init(double LXlo, double LXhi) throws <font color="#ff0000">LuckOutException</font>;
      protected abstract double GetX();
      protected abstract String PrintState();
      private boolean verbose=false;
      protected double eval(double x) throws <font color="#ff0000">LuckOutException</font> {
        double result=R.F(x)-Yt;
        if(IsConverged(result)) throw new <font color="#ff0000">LuckOutException</font>(x);
        return result;
      }
      protected double evalp(double x){
        return ((RootDeriv)R).dFdx(x);
      }
      public double Find(double LYt, double LXlo, double LXhi) throws ArithmeticException {
        Yt=LYt;
        try {
          init(LXlo,LXhi);
          int IterLimit=100;
          int iters=0;
          if(verbose)System.out.println(PrintState());
         
          while(!IsConverged(Step())) {
            if(verbose)System.out.println("Iter: "+iters+"\n"+PrintState());
            iters++;
            if(iters>IterLimit) throw new ArithmeticException("Convergence failed after "+IterLimit+" iterations");
          }
          return GetX();
        } catch (<font color="#ff0000">LuckOutException</font> E) { return E.x; }
      }
    }
    </font>
    <font face="Courier New">package ***.rootfind;

    /

     
    Root-finding algorithm hit early convergence. Not really
     
    an error. The RootFind framework traps this exception and
     
    uses the double value in it as the independent variable.
     
    /
    public class LuckOutException extends Exception{
      /

       * Independent variable which happens to be sufficiently
       * close to be a root.
       */
      double x;
      /

       * Constructs a new LuckOutException with the value which
       * happens to be a root.
       * @param Lx Independent variable value
       */
      public LuckOutException(double Lx) {
        super(
          "Algorithm hit early convergence at independent variable value: "+
          Double.toString(Lx)
        );
        x=Lx;
      }
    }

    <font face="Times New Roman">What's going on is that as this root finder hunts around, it may happen that it hits on a value of x which is the correct answer, purely by accident, before it is intended to converge. This is an ok situation, as all I care about is that the answer is correct.

    So, I have a LuckOutException which is thrown in these cases. This exception has an extra field, where the algorithm sticks the correct answer when it hits it, and the catch block just returns that field as the correct answer.

    1) Is this a bad way to do this?
    2) If so, what should I do instead?

    </font></font>



  • Relying on errors as way of proper program exit is the Brick Wall Approach. You want a car to stop? Just drive it into a wall!

    Instead of making the program end and return what it's supposed to return by free will, you're making it trip, fall on its face, and mug it of the answer while it's down.

    Now, if it's truly an [i]Exceptional[/i] situation that triggers the LuckOutException, then it's ok.

    But you'd agree it's odd program design if an exception can still return a proper result. As though, sometimes when the program trips, the wallet falls out on its own. :)

    I'd rethink the program design to accomodate some concept of Luck proper.



  • If you really need to distinguis between the case of an exact 0, or a "probably converges to 0", maybe you should have a class:

    public class Root {
        double x;
        boolean luckedOut;
        // getters/setters/constructors, etc.
    }

    Then, return new Root(x, true) when you luck out, or return new Root(x, false) if you don't.

    If you DON'T need to know whether you "lucked out" or not, then simply return x alone without writing the "Root" class.

    Also, a note on style... the RootFunction class (I hope its really an interface) maybe should be called something like "Function", and R (which would now be of type Function, not RootFunction" should have a better name, like "function" (hmm, that seems too obvoius maybe?)

    RootDeriv maybe better named as "Differentiable" (correct spelling mistakes I make, please)

    Also, R.F(x) should be changed to "function.evaluate(x)"
    AND, ((RootDeriv)R).dFdx(x) should be changed to "((Differentiable)function).derivativeAt(x)"

    Remember, Exceptions are "Exception (failure) conditions" which a program isn't necessarily expected to recover from.  Getting the answer on the first try hardly seems like something unrecoverable.

     



  • I don't think this is really so terrible.  The concept of a non-local exit is perfectly sensible, and AFAIK the only way to do it in Java is with try/catch/throw.  Some people seem to object to the use of the word "Exception" to describe something that isn't exceptional, but that's easy to work around:

    <FONT face="Courier New">public class LuckOut extends Throwable {</FONT>

    See?  No exceptions in sight! ;-)



  • @dhromed said:

    Relying on errors as way of proper program exit is the Brick Wall Approach. You want a car to stop? Just drive it into a wall!

    Instead of making the program end and return what it's supposed to return by free will, you're making it trip, fall on its face, and mug it of the answer while it's down.

    Now, if it's truly an [i]Exceptional[/i] situation that triggers the LuckOutException, then it's ok.

    But you'd agree it's odd program design if an exception can still return a proper result. As though, sometimes when the program trips, the wallet falls out on its own. :)

    I'd rethink the program design to accomodate some concept of Luck proper.

    Seen all-to often on the homepage of this site:

    <FONT color=#ff0000>Error:  The operation completed successfully.  To resolve this situation, please call RootFinder tech support at 1-800-123-4567</FONT>



  • @Albatross said:

    @dhromed said:

    Relying on errors as way of proper program exit is the Brick Wall Approach. You want a car to stop? Just drive it into a wall!

    Instead of making the program end and return what it's supposed to return by free will, you're making it trip, fall on its face, and mug it of the answer while it's down.

    Now, if it's truly an [i]Exceptional[/i] situation that triggers the LuckOutException, then it's ok.

    But you'd agree it's odd program design if an exception can still return a proper result. As though, sometimes when the program trips, the wallet falls out on its own. :)

    I'd rethink the program design to accomodate some concept of Luck proper.

    Seen all-to often on the homepage of this site:

    <font color="#ff0000">Error:  The operation completed successfully.  To resolve this situation, please call RootFinder tech support at 1-800-123-4567</font>



    This seems to be assuming that all Exceptions must get reported to the user. This is not the case. Some exceptions can be recovered from successfully. This is one case. Another is an error of a database connection being already closed when you try to close it( if(!conn.isClosed())conn.close(); ) is not thread-safe, but it's not a big deal if any generated exception is trapped.


  • @MntlChaos said:

    @Albatross said:

    @dhromed said:

    Relying on errors as way of proper program exit is the Brick Wall Approach. You want a car to stop? Just drive it into a wall!

    Instead of making the program end and return what it's supposed to return by free will, you're making it trip, fall on its face, and mug it of the answer while it's down.

    Now, if it's truly an [i]Exceptional[/i] situation that triggers the LuckOutException, then it's ok.

    But you'd agree it's odd program design if an exception can still return a proper result. As though, sometimes when the program trips, the wallet falls out on its own. :)

    I'd rethink the program design to accomodate some concept of Luck proper.

    Seen all-to often on the homepage of this site:

    <font color="#ff0000">Error:  The operation completed successfully.  To resolve this situation, please call RootFinder tech support at 1-800-123-4567</font>



    This seems to be assuming that all Exceptions must get reported to the user. This is not the case. Some exceptions can be recovered from successfully. This is one case. Another is an error of a database connection being already closed when you try to close it( if(!conn.isClosed())conn.close(); ) is not thread-safe, but it's not a big deal if any generated exception is trapped.


    True that not all Exceptions should be reported, however, Exceptions (ANYTHING that extends Throwable) should generaly be thrown only in situations where the direct caller might reasonable be expected to be unable to handle the situation.

    As for this particular case, it doesn't seem that it matters if the result is "lucky" or not, so its important to realize that you can "return" a value from any point in a method, even within a conditional, without use of exceptions.


  • Like it is posted before, exceptions are for exceptional or unintended behaviour.
    If finding a correct answer constitutes non-normal conditions for your class/method then an exception is justified.
    But also remember the principle of encapsulation. You don't want to bother users of your object (be it programmers or other code) with details of how your object obtained it's result. A succesful result should always be communicated in the same way. And I can't think of one situation in which you want to communicate a success result by using an exception.



  • @danielpitts said:


    True that not all Exceptions should be reported, however, Exceptions (ANYTHING that extends Throwable) should generaly be thrown only in situations where the direct caller might reasonable be expected to be unable to handle the situation.

    As for this particular case, it doesn't seem that it matters if the result is "lucky" or not, so its important to realize that you can "return" a value from any point in a method, even within a conditional, without use of exceptions.


    I understand that you can put a return anywhere you want. What I am doing here is trying to return up several layers in the call stack at once. (return to the caller of the caller of the caller...) The exception mechanism is a perfect, controlled way of doing this, and this code does exactly what I want, but there's that little word "exception." I guess, to rephrase my question, is there a clean, easy way to return up several layers in the call stack all at once, without using an exception. It would seem that there is not. So, I just took the advice of the guy who said "don't call it an exception."



  • Why yes, you could do that. It is perfectly possible to misuse any mechanism in java.
    You've already misused inheritance where a strategy pattern is in order, so why not the exception mechanism as well.



  • @El Bastardo said:

    Why yes, you could do that. It is perfectly possible to misuse any mechanism in java.
    You've already misused inheritance where a strategy pattern is in order, so why not the exception mechanism as well.

    lol.

    Which is probably the cause of his desire to misuse the exception mechanism.

    More to the point, if you really want to return to a place further up the call stack, its best (truely more elegant, even if it doesn't seem so), to check and return at every level.  Using exceptions as a  kind of "flow control" is very bad in almost all cases.

    Using exceptions as "error handling" is good in most cases.




  • The design with the exception is also not good for a completely
    different reason. This is that an exception is introduced to deal with
    a very special case, namely accidentally finding the solution to the
    problem.



    If you would leave out the entire mechanism with the exception, then
    the algorithm would probably still work. It looks like trying to
    optimize for a very special case. By the way, I have worked extensively
    in numerical mathematics with a focus on iterative solution methods and
    I never saw any code to deal with the extremely special case of
    accidentally finding the solution. If your algorithm is good you can
    just leave out the check.



  • My experience is the exact opposite. It almost always hits LuckOut rather than iterating to the convergence criterion, in that I have never watched the algorithm run to convergence before lucking out. You are right, without this code it would work just fine, but it seems like when you've found what you are looking for, you should stop looking.



  • @kwan3217 said:

    My experience is the exact opposite. It almost always hits LuckOut rather than iterating to the convergence criterion, in that I have never watched the algorithm run to convergence before lucking out. You are right, without this code it would work just fine, but it seems like when you've found what you are looking for, you should stop looking.


    Right, you should stop looking, but not throw the what you found out the window and hope someone catches it.



  • @kwan3217 said:

    My experience is the exact opposite. It almost always hits LuckOut rather than iterating to the convergence criterion, in that I have never watched the algorithm run to convergence before lucking out. You are right, without this code it would work just fine, but it seems like when you've found what you are looking for, you should stop looking.

    Then why not structure your search loop like this:

    <FONT face="Courier New" size=2>setupInitialValues();
    while (!solutionFound() && !searchLimitReached()) {
      approximateBetter();
    }
    if (!solutionFound) throw new LimitReachedException();
    return getResult();</FONT>

    Using the exception is not necessary for an early abort.

    For the general case: using exceptions in normal flow is kind of bad form. But I'm a pragmatist, so I'd say: "if it works and it makes your life easier, why not use it?"



  • @RiX0R said:

    Then why not structure your search loop like this:

    <font face="Courier New" size="2">setupInitialValues();
    while (!solutionFound() && !searchLimitReached()) {
      approximateBetter();
    }
    if (!solutionFound) throw new LimitReachedException();
    return getResult();</font>

    Using the exception is not necessary for an early abort.

    For the general case: using exceptions in normal flow is kind of bad form. But I'm a pragmatist, so I'd say: "if it works and it makes your life easier, why not use it?"



    FINALLY a helpful response! This actually makes sense, is significantly better than what I had, and can be implemented. And, you didn't imply I was an idiot. Thanks a bunch!


  • nvm.



  • @kwan3217 said:

    @RiX0R said:

    Then why not structure your search loop like this:

    <font face="Courier New" size="2">setupInitialValues();
    while (!solutionFound() && !searchLimitReached()) {
      approximateBetter();
    }
    if (!solutionFound) throw new LimitReachedException();
    return getResult();</font>

    Using the exception is not necessary for an early abort.

    For the general case: using exceptions in normal flow is kind of bad form. But I'm a pragmatist, so I'd say: "if it works and it makes your life easier, why not use it?"



    FINALLY a helpful response! This actually makes sense, is significantly better than what I had, and can be implemented. And, you didn't imply I was an idiot. Thanks a bunch!


    I don't want to bitch, but rixor's answer is applying basic java knowledge.  Nobody probably even imagined you would not have known about this alternative.

Log in to reply