[2330] in java-interest

home help back first fref pref prev next nref lref last post

Re: throws declataration in Java/beta

daemon@ATHENA.MIT.EDU (tmb@best.com)
Fri Sep 29 09:39:06 1995

From: tmb@best.com
Date: Fri, 29 Sep 1995 04:12:57 -0800
To: flar@bendenweyr.Eng.Sun.COM, garya@village.org
Cc: java-interest@java@sun.com

I mentioned before that I had observed that exception declarations seem like
a neat idea but are unpopular in practice.  Thinking about it, I also think I
have figured out why now.

I think this is most clearly explained in a functional programming
context.  Consider an "apply" function:

    fun apply(f,x) = f(x)

What set of exceptions should "apply" be declared to raise?  It should
really be the set of exceptions that "f" can raise.  So, the set of
exceptions depends on one of the arguments.  No more and no less is
"correct".  Unfortunately, unless you make exceptions part of a
function's type and you have some kind of template or type variable
mechanism, you can't do this.

Why is this relevant to Java?  After all, people don't use "apply" in
Java.  It turns out that analogous constructs are actually quite
common in object oriented programs, in the guise of callbacks,
iterators, streams, generators, mappers, and just about anything else
that is somewhat abstract.

Consider the following simple example (pseudocode):

    interface Generator {
	boolean done();
	int get();
    };

    class Twice implements Generator {
	Generator source;
	Twice(Generator source) { this.source = source; }
	public boolean done() { return source.done(); }
	public int get() { return source.get()*2; }
    };

Now, the methods "get" and "done" in "Twice" should really declare that
they return exactly the exceptions that their corresponding "source"
generator methods might raise.  But that set of exceptions depends on
the type of "source".  For example, if "source" is a generator that
generates the natural numbers, it may not raise any exceptions.  If it
is a generator that reads from a file or a network connection, it might
raise an I/O exception.  And it is unreasonable to expect "Twice" to be
able to handle those exceptions; the caller that instantiates "Twice"
and uses it is the one that knows about how to handle those exceptions.
In terms of the set of exceptions it can raise, "Twice(g)" is really
exactly the same kind of stream as "g" itself.

The same kind of problem occurs if we define "Twice" somewhat differently:

    class Twice implements Generator {
	public boolean done() { return source.done(); }
	public abstract int get_one();
	public int get() { return get_one()*2; }
    };

Now, rather than setting the value of "source", the kind of generator is
defined by implementing the method "get_one".  The problem is the same:
the set of execptions that can reasonably be thrown by "get" depends on
what kind of use we put "Twice" to.

Maybe the above examples seem a little abstract and not very pertinent
to real programs.  I think if you look at the way inheritance and
forwarding is used in lots of OO programs, you will find that similar
constructs are quite frequent.  I also suspect that numerous exception
declarations in the new Java sources may actually be too restrictive.
Unfortunately, those are subtle design errors that are difficult
to localize and that won't be recognized for a while.

If you truly want to get this right, you need some kind of type
variables (like SML) or templates (like C++), together with function
types that include the set of raisable exceptions as part of their type.

If you don't have this kind of support in the language, then any kind of
class that implements a kind of abstraction similar to "Twice" in the
example above really has to be allowed to raise any exception.  Worse
yet, if you are too restrictive in the set of exceptions you declare for
a class like "Twice", you are going to cause users of your class library
lots and lots of headaches later on.

I must admit, I find the introduction of exception declarations into
Java quirky.  In a language like SML, with its powerful static type
system, obsession with static checking, and type inference, exception
declarations might be both justifiable and reasonably convenient (but
even SML doesn't have them!).  

Java, however, does not have a particularly sophisticated static type
system.  It makes up for the limitations of its static type system by
using dynamic typing.  That's a good compromise for an easy-to-use,
easy-to-implement language; that compromise is commonly made and has
been found to be very livable in practice.

The analogous compromise for exceptions is to live with the fact that
you can't know at compile time what set of exceptions a method might
throw.  Practical experience from other languages suggests that you
should either not bother with exception declarations at all, or at least
make the default "may throw any exception".

The more I think about it, the more I get convinced that exception
declarations in Java are a bad idea: wrong target audience and
insufficient support by the type system.  If you stick with it,
this may be a headache for years to come.

			       Thomas.
-
Note to Sun employees: this is an EXTERNAL mailing list!
Info: send 'help' to java-interest-request@java.sun.com

home help back first fref pref prev next nref lref last post