....
I'd disagree with that assessment. It's certainly not a kludge. It may
be a pain at times, but it's exactly what you ought to expect to do.
Failing to throw exceptions that are appropriate for the interface
you're designing is poor abstraction. It is *exactly* like writing a
"lookupEmployee" method in a payroll system that returns a complete
payroll history and expects you to poke around and grab the information
about the employee from that. Both are examples of breaking abstraction
boundaries, and both are poor code.
Hmm, I don't know. Wrapping up one exception type in another seems to
me to be verging on the kludge particularly if the /reason/ for
wrapping it up is to pass it up the calling chain silently and thus
avoid an inbuilt mechanism of the language. I completely agree with
the principle of information hiding - Parnas' principles and all that.
I'm not convinced the analogy is *exactly* apt. Maybe the converse.
Checked exceptions seem like the employee record forcing any code that
reads it to examine all fields whether they are relevant to its
purpose or not.
To understand this bit of language design, you need to get your head
around the fact that declaring methods isn't some boilerplate thing that
you do because the compiler makes you. A method declaration is a
contract with a caller, and the fact that certain errors can be
generated is part of that contract. Another example: suppose that,
early on in the development of some system, you stub out some function.
You know that eventually you'll need to write that method. The throws
clause lets you ensure that the rest of your code properly handles the
error cases that will exist in that method later on. Even though you
have no implementation, you can still write to the contract.
Fair enough. I can see the value of this in development and, you may
be surprised to know, I like this feature. If I can declare exceptions
that will be generatable later I can build the checking now and come
back to actually throwing the exceptions and testing later. Good! I
still want to handle them where appropriate, though, not necessarily
having to include code for them at the lowest level. If the compiler
could warn me of any declared but not yet throwable exception so much
the better. It would be a good reminder that work is required.
If that's true, then you need only throw Exception. There are, of
course, reasons why that is strongly discouraged... but they are EXACTLY
the same reasons that checking exceptions is considered desirable. For
example, I frequently declare main methods of test code with
"throws Exception" tacked onto the end; it makes sense to do so because,
as you said, it's okay for that code to generate arbitrary errors. It's
not generally okay for more significant pieces of software to generate
arbitrary errors; but if it is, then by all means you are free to add
"throws Exception" there, too.
I think you misunderstand me. I wasn't saying that one should catch
Exception but that it is reasonable to accept that arbitrary things
can go wrong in any called module. Java's mechanism is, at best,
partial.
This would, of course, provide none of the main benefit of checked
exceptions, which is the guarantee that error conditions are handled
(even across later changes, for example, for maintenance). The
documentation would just be silently generated and you would have no
idea about the time bomb in your code that's just waiting for someone to
do something wrong.
What if the requirement was that checked exceptions had to be handled
just "somewhere above" the called module, if nowhere else, then in
main (for an application)? This ensures the compiled code handles all
declared generatable exceptions.
That being said, though, I think you have a little of a point. It would
make sense to be able to have the compiler infer the throws clause for
certain methods, such as private (perhaps even package protection)
methods in a class. Adding such a thing would certainly encourage more
decomposition of methods, which is a good thing. I'm afraid, though,
that you're proposing that we throw the baby out with the bathwater;
removing checked exceptions for public or protected methods on classes
that are used from outside of their package would be missing out on a
significant advantage of the language.
How about simply allowing the catching to be done higher up?
Do you have any basis for this? Lots of people have different tastes
with regard to how much static validation should be performed on code;
but you should at least recognize that you're drawing the line rather
arbitrarily. Have you substantially used languages with much stronger
type systems (say, ML or Haskell)? Do you propose any specific criteria
for making the decision as to whether certain static validation features
are beneficial or not?
I think the comment of mine that you snipped was a valid example! If
I'm wrong and you didn't snip it the example I was thinking of was
Pascal's treatment of array dimensions as part of the array type. Type
safety gone too far. As another example, say I define a complicated
function to work on two integers and return a third. I really don't
want to write separate functions: one for bytes, one for 16-bit
values, one for 32-bit and one for 64-bit. I want to define just one
for integers and then perhaps state the types for which it applies.
Now, how many pieces of machine code does that need? If I am not
needing to check for overflow etc I may allow anything less than the
word size of the host machine to be handled as words. For example, on
a 32-bit machine, from the same single piece of source code I may
generate executable code for just two types: 32-bit integers and 64-
bit integers. It (the 32-bit version) can still run on integers of 8
or 16 bits as long as the returned value is taken from the lower bits
of the result. Thus I want the control of selecting arguments based
on /partial/ type information.
Your question about my criteria for selecting static validation
features is a good one. I don't have a complete picture yet. I am
planning to allow the programmer the choice in more areas than are
traditional in programming languages. I don't really want to go too
far off topic on this as some things are a discussion in themselves
but to pick one example: Languages generally mandate either structural
equivalence of types or name equivalence. Once decided by the language
creator the decision is fixed; this is a very early binding time. I
wonder if there is some way the programmer could simply specify which
types are compatible and it what ways. The type safety is still there
and the programmer has the control needed. Taking it away from this
specific example and back to the general I don't want to mandate too
many static validation features aritrarily but I still want to make
them available so they can be used by the programmer if needed. I see
the programmer as a customer, here, and the language as a product,
with all the balancing acts that requires. In practice there would be
another customer: the programmer's employer (or supervisor or
customer, as appropriate). The employer may have coding standards in
to which the produced code is expected to fit. Therefore coding
standards would be enforceable and could be used to restrict certain
options. In this way the flexibility is allowed where needed but
bounds may be set.