20 February 2010

Forwarding exceptions as language potential

Each of Java programmer knows how exhausting a handing exception can be. So sooner on later he/she founds the situation when he/she exactly knows that he/she is handling the exception that will never occur. Some thoughts about this can be found here.

Negative consequences

What are negative consequences of obligation to handle the checked exception:

  • They are omitted in order to simplify code (use only unchecked exceptions or catch and ignore checked exceptions).
  • A few exceptions are exchanged in one. This is considered as a good style, but mostly I don't think about it in this way.
  • There are lots of cases when we know that exception will not occur, but we cannot simply left catch block blank, because it can be changed in future.

At first, I wanted to handle it by generics, but tracking such a code might be almost impossible, however, at the end, in mostly cases, it may make code complication increased. Someday in the future, I'll try to describe the matter of generic exception.

What do we have today?

Quite interesting is the fact that Java allows to omit checked exceptions that have no chance to occur - what is widely used in practice. Let's see, how this works right by using the classes similar to java.lang.Appendable & java.lang.StringBuilder.
As the base, let's declare interface:

public interface Appendable {

    Appendable append(CharSequence csq) throws ParamaterException;

}

Let's consider the class that uses this interface:

public class StringBuilder implements Appendable {

    private String string = "";

    public Appendable append(CharSequence csq){
    string += csq;
    return this;
    }

}

Using StringBuilder class directly through its interface ( Appendable ) we have to handle the exception:

static{
    Appendable stringBuilder = new StringBuilder();
    stringBuilder.append("");
}

Let's use direct implementation StringBuilder class. Now we do not have to handle that exception.

static{
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("");
}

Let's consider more complicated example:

public class Sample {


    private Data data;

    Appendable appendTo(Appendable appendable) throws ParamaterException, ObjectException {
        if (data == null) throw new ObjectException();
        appendable.append(this.getClass().getSimpleName());
        return appendable;
    }
}

So as you can see one exception ( ObjectException ) comes from a called object, second one ( ParamaterException ) comes from a method parameter ( appendable ).
Let's modify the method signature in order to allow the complier to know where each exception comes from.
Let's modify semantics to express that:

public class Sample {


    private Data data;

    Appendable appendTo(Appendable appendable throws ParamaterException) throws ObjectException {
        // Both exception are thrown just ParamaterException is thrown/forwarded only if it occurs by calling 'appendable'.
        if (data == null) throw new ObjectException();
        appendable.append(this.getClass().getSimpleName());
        return appendable;
    }
}

Let's assume that placing the throw declaration right after input parameter means that the exception can be thrown only by the calling given parameter.

So in this way, the compiler can determine if the exception can be thrown by the known given parameter type or not

static{
    new Sample().appendTo(new StringBuilder()); // Compiler is able to determine that ParamaterException will do not occur here.
}


The above mentioned way to handle exception in language is a quite simple to understand and introduce. However, it's just a tip of the iceberg. If sometimes we have a nice way to handle exception flow in a this way of solving, this will have to be extended what can bring some problems in a few other aspects, however, it's not now time to bring it up.

At the end, I would like to mention that I'm just afraid that most programmers will not be able to comprehend the exceptions and the application flow in the same time.

9 comments:

  1. You already have the answer. You only need to look at code from the OO paradigm;P

    Look at Your examples...
    second one:
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("");

    If You create an object and than use it in the SAME block of code that obviously You don't need an abstraction, so You don't use polymorphism. That's ok.

    first example:
    Appendable stringBuilder = new StringBuilder();
    stringBuilder.append("");

    has NO SENSE at all. Why do You use polymorphism? If creating an object and using it are in the same block than its crazy. You don't need an abstraction.

    So, If You follow firsts pattern (no abstraction) than Your problem does not exists. Some special impl may simply declare that it does not throw anything.

    But it was trivial case.

    Consider real one:

    class HigherLevel{
    private LowerLevelInterface lowerLevel;
    //setter or construction (for injection for example)

    public void play(){
    lowerLevel.callMethodThatCanTrowException();
    }
    }
    In this class of problem You have no guarantee about exception. Of course You may assume that LowerLevel impl won't throw exception. Because You simple know that what type of impl is used and what it does. But in this case... sorry man... You are not laying in OO way if You use polymorphism but actually know about impl;P
    Interface is just a contract and You must obey it and don't try to "hack" and guess what really happens in impl.

    Just use another construction, another language and stop worrying;)

    Summary: look from the bigger perspective, than You see that syntax is at really coherent.

    ReplyDelete
  2. @Sławek Sobótka
    "You don't need an abstraction ..."

    As I see I should more clearly describe this code's purpose. Using the abstraction is a first step to extract this part of code to the method, what is very OO if we are going to reuse it. What's more, I've already found that using lower-level implementation just in order to avoid unpleasant exception will do not work for a long time.

    A few times I used StringBuilder, and it worked fine till its size started to exceed 500Mb. For me, loosing memory up to 1Gb do not look as any option. So as long as all required methods are in the interface, the interface should be used.

    ...

    I've never said that I would hack any contract. At the level in which this information is not available in compile time, the code would work in the same way in 100%.

    So let's look at the real word to see how it works like. When you are giving back bike to mechanic, it's natural for you do not expect the problem like "engine is not available at market now", because there is no engine in it. There's the same situation when you pay in store with paper money. You do not expect to hear "Sorry we cannot accept your credit card."

    As for me, OO should help to map real work into code when you suggest quite a different path.

    ReplyDelete
  3. Just to be sure we are not loosing connection with reality:
    What is the common interface of Bike and Car? I'm asking about interface that makes sense in the context of MechanicMan? Repairable?:P
    If mechanic can throw EngineNotAvailableException than You have signed wrong contract with mister Mechanic.

    That's my point: wrong design leads to artificial problems.

    Btw: what does 1GB of memory have to do with good design?

    ReplyDelete
  4. @Sławek Sobótka

    Common interface of Bike and Car is SomethingMechanic.
    So should we have available thousands contracts/methods instead of relaying on the common interface (if it's only possible)?


    For me, using StringBuilder instead of Appendable resulted in the situation when all data had to be built in the memory, because I was not able to use Writer nor PrintStream.

    ReplyDelete
  5. Does SomethingMechanic implicate having an engine? If no, than look for another mechanic or change design so that Mechanic throws NoPartsAvailable with some parameter (maybe enum) that carry information about problematic part.

    Now I understand Your particular problem. Finally I figured it out:) You need sometime StringBuilder and sometimes some Stream. Well.. thats just stupid design of standard library (not the only one hehehe) - appending string to the Builder may cause IOException - very nice Sunny boys:)

    So your solution to patch wrong design is to wrapp library code with Your own class that throws RuntimeException maybe.

    ReplyDelete
  6. When I FIRST read this post (without any comments) I thought I understand the problem:
    1. The author has a problem with unnecessary exception handling with given examples (Appendable and Stringbuilder classes)
    2. The author suggested a solution which can be a workaround for exception handling.

    THEN I read comments. And I realised that I understood the problem and the author in 50%. Why? Because the problem comes from WRONG DESIGNED library classes, and that's why author suggested a workaround for this case.

    Moreover, it should be read in that order: wrong design -> problems in code -> suggested workaround.
    But now it looks like: I would like to have some new language syntax tricks, in order to make my code easier to write. Why? Because when using classes Appendable and StringBuilder I found some inconvenients.

    My suggestion for the author: You should explicite write some sentences which will give Your readers a cintext of a problem. Have no fear for using bold, red font. Place them after those 3-points. The sentence should be something like this:

    "Some built-in java classes forces us to handle exceptions which never should occur. Why? Because they are wrong designed. For example Appendable class and its implementation StringBuilder. When we use StringBuilder through its interface Appendable, we have to handle IO exception for the method append(). And this is WRONG - StringBuilder operates in memory, but we have to handle some IO expcetions, even if we know that those exception will never occure for the StringBuilder class".

    ReplyDelete
  7.    I do not like to give you samples, because then you focus on particular problem. This one (as most others problems) have a few solutions, and it does not matter here if we have another good one. For me, more important is the fact if the suggested solution increases overall complication of the whole program or not.

       Although you are aware of the fact that every program can be written using only one loop, it does not mean that using more loops is something bad. The same situation is here: if we allow design to solve all problems in the word how much effort will it take to solve them?

    Language has to be enough fluent to allow single problem to be solvable even with little lack of design.

    Anyway, I do not consider this particular Java design as something wrong.

       So please, do not use golden hammer everywhere, and at first consider if average complication of solving problems will decrease or increase.

    ReplyDelete
  8. 1. I am focusing on conceptual errors in examples. Error in each example... strange:P

    2. Role of a language... Should it prevent amateurs form bad design or allow experts to do everything they want? Should syntax be like grammar in human language preventing mumbo jumbo? It depends on intention of language...
    Java intention is different than C or Perl.

    3. I do consider wrong design when general interface forces to catch Specific Technical Exceptions. It is against SOLID principle: Interface Segregation Principle (ISP).

    4. Simplicity is what every programmer desires the most. There is just difference in meaning of simplicity. And believe me - I am the last one who search for golder hammer or silver bullet or shit;)

    From my perspective: End of Topic, I really have nothing to add and I give up, you won:)

    ReplyDelete
  9. @Sławek Sobótka

       I do not care if there is conceptual error as long as it do not affect problem that I describe.


       Don't think that I'll give up on you so easy. ^__^

    ReplyDelete