Discussion:
Exception programming difficult
(too old to reply)
Marco Leise
2012-08-12 03:02:25 UTC
Permalink
I just got a bit frustrated and wanted to say that I like working with Exceptions in Java a lot more. That has to do first but not foremost with the declaration:

---Java->>

class MyException extends Exception {
public MyException(String msg) {
super(msg);
}
public MyException(String msg, Throwable next) {
super(msg, next)
}
}

<<-Java---
---D->>

class MyException : Exception {
this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
super(msg, file, line, next);
}
this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) {
super(msg, file, line, next);
}
}

<<-D---

The other think that I'd really like to see is an actual declaration for the user of a function to see what exceptions can be thrown - also inferred and checked by the compiler. In Java you cannot compile a function that throws some (checked) exception and doesn't declare that like this:

---Java->>

void foo() throws MyException {
throw new MyException("test");
}

<<-Java---

This way you, as the author, are always aware of which Exceptions you handle inside the function and which may escape. This escalates to callers of the function as well, so that in the end the public API has an exact list of possibly thrown exceptions that the user must handle.
I have around a dozen different exceptions in a hierarchy and a few nested function calls. It's a maintenance horror to keep the DDoc up to date about thrown Exceptions. It also doesn't check the spelling or offers a link to the Exceptions.

---D->>

/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* Params:
* response = receives the whole response
* Throws:
* UnexpectedResponseException if the server sent us garbage
*
* UnauthenticatedException we need retry after we have logged in
*
* SecurityException we have to switch to a secure connection for this
*
* DisconnectException the connection was unexpectedly terminated
* Returns: the associated response code
*/

<<-D---

I know that the Java way isn't perfect, because some lazy people write dummy exception handlers to silence the errors, but its a worse solution to _not_ notify the user of a function, that it potentially throws exceptions, I think. So I wish D was explicit about thrown Exceptions.
--
Marco
Marco Leise
2012-08-12 03:28:35 UTC
Permalink
Am Sun, 12 Aug 2012 05:02:25 +0200
Post by Marco Leise
---D->>
/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* response = receives the whole response
* UnexpectedResponseException if the server sent us garbage
*
* UnauthenticatedException we need retry after we have logged in
*
* SecurityException we have to switch to a secure connection for this
*
* DisconnectException the connection was unexpectedly terminated
* Returns: the associated response code
*/
int foo(out string response)
{...}
<<-D---
could become:

---D->>

/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* Params:
* response = receives the whole response
* Returns: the associated response code
*/
int foo(out string response) throws
UnexpectedResponseException, /// if the server sent us garbage
UnauthenticatedException, /// we need retry after we have logged in
SecurityException, /// we have to switch to a secure connection for this
DisconnectException /// the connection was unexpectedly terminated
{...}

<<-D--

So there would be no net change in typing required if that is a concern. Callers of the function could inherit from this documentation as well for their "throws" section, so you only have to rewrite the documetation if the reason for the exception needs to be formulated differently or multiple called functions throw the same exception.
--
Marco
Dmitry Olshansky
2012-08-15 19:29:43 UTC
Permalink
Post by Marco Leise
Am Sun, 12 Aug 2012 05:02:25 +0200
Post by Marco Leise
---D->>
/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* response = receives the whole response
* UnexpectedResponseException if the server sent us garbage
*
* UnauthenticatedException we need retry after we have logged in
*
* SecurityException we have to switch to a secure connection for this
*
* DisconnectException the connection was unexpectedly terminated
* Returns: the associated response code
*/
int foo(out string response)
{...}
<<-D---
---D->>
/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* response = receives the whole response
* Returns: the associated response code
*/
int foo(out string response) throws
UnexpectedResponseException, /// if the server sent us garbage
UnauthenticatedException, /// we need retry after we have logged in
SecurityException, /// we have to switch to a secure connection for this
DisconnectException /// the connection was unexpectedly terminated
{...}
<<-D--
When I see code like this I have one single thought - error codes!

Indeed that's what is used in this example, with Exceptions only being
convenient (and separate) transport for error codes. So the net progress
is creating 1:1 type for each error condition (start counting the lines
of code) and then...

If I got it right somewhere later foo is supposed to be used like this:
try{
...some_code
foo();
...other code
}
catch(UnexpectedResponseException)
{
print error and disconnect this server or retry?
}
catch(UnauthenticatedException)
{
print error 2
}
catch(SecurityException)
{
sslFoo(resp); // this one is just awesome ;)
}
catch(DisconnectException )
{
print error 3 & (ask to) reconnect?
}

Or used a catch all and do type switch Java-style to see if Exception is
one of interesting to you types. Needless to say awful again.

First SecurityError is gross fiction as you either know to authenticate
(need credentials in the interface) or do auto detection (like try
HTTPS, then fallback to HTTP).

Moreover encoding _cause_ of error in type is useless, end user needs a
hint on the proper way to handle error. It's like pointing out a guy who
made the mistake and how stupid it is instead of proposing the ways to
fix the situation.

what I'd expect the code to be is (been discussed before):
class NetworkException
{
@property bool transient; // packet lost or whatever, can re-try with
the same parameters
@property bool lost; // need reconnect to restore, server down or
disconnected or unexpected input
@property string msg();
}

This reduces all the handling to:
catch(NetworkException ne)
{
if(ne.lost)
//ya kill me, but you got the idea ;)
goto ReconnectAndRetry;
if(ne.transient){
warning("..."); //log failure
goto RetryAndCheckTryCount;
}
error(ne.msg);
}

Including but not limited to the time when foo's author adds more types
to his "throws list". Unlike checked exceptions it won't break build
just for the fuck of it *and* it will still work correctly.

In fact if we manage to come up with proper reasonable standard
exceptions like Network/IO/etc. that everybody derives from error
handling would become damn easy with *any* library.
--
Olshansky Dmitry
Marco Leise
2012-08-17 07:47:32 UTC
Permalink
Am Wed, 15 Aug 2012 23:29:43 +0400
Post by Dmitry Olshansky
When I see code like this I have one single thought - error codes!
Indeed that's what is used in this example, with Exceptions only being
convenient (and separate) transport for error codes. So the net progress
is creating 1:1 type for each error condition (start counting the lines
of code) and then...
try{
...some_code
foo();
...other code
}
catch(UnexpectedResponseException)
{
print error and disconnect this server or retry?
}
catch(UnauthenticatedException)
{
print error 2
}
catch(SecurityException)
{
sslFoo(resp); // this one is just awesome ;)
}
catch(DisconnectException )
{
print error 3 & (ask to) reconnect?
}
Or used a catch all and do type switch Java-style to see if Exception is
one of interesting to you types. Needless to say awful again.
First SecurityError is gross fiction as you either know to authenticate
(need credentials in the interface) or do auto detection (like try
HTTPS, then fallback to HTTP).
Moreover encoding _cause_ of error in type is useless, end user needs a
hint on the proper way to handle error. It's like pointing out a guy who
made the mistake and how stupid it is instead of proposing the ways to
fix the situation.
class NetworkException
{
@property bool transient; // packet lost or whatever, can re-try with
the same parameters
@property bool lost; // need reconnect to restore, server down or
disconnected or unexpected input
@property string msg();
}
catch(NetworkException ne)
{
if(ne.lost)
//ya kill me, but you got the idea ;)
goto ReconnectAndRetry;
if(ne.transient){
warning("..."); //log failure
goto RetryAndCheckTryCount;
}
error(ne.msg);
}
Including but not limited to the time when foo's author adds more types
to his "throws list". Unlike checked exceptions it won't break build
just for the fuck of it *and* it will still work correctly.
In fact if we manage to come up with proper reasonable standard
exceptions like Network/IO/etc. that everybody derives from error
handling would become damn easy with *any* library.
Just for the fuck of it, huh :) ? Interesting read, do you know an example, probably a standard library of some other language, that offers standard exceptions like this? If I understand you correctly, the idea is to not use distinct error ids (be they codes or exception classes), but a mix of flags. So you'd have pretty much one "catch" with a few "if"s, but with the flags you can decide on the granularity.
--
Marco
Dmitry Olshansky
2012-08-17 08:48:01 UTC
Permalink
On 17-Aug-12 11:47, Marco Leise wrote:
[snip]
Post by Marco Leise
Post by Dmitry Olshansky
Including but not limited to the time when foo's author adds more types
to his "throws list". Unlike checked exceptions it won't break build
just for the fuck of it *and* it will still work correctly.
In fact if we manage to come up with proper reasonable standard
exceptions like Network/IO/etc. that everybody derives from error
handling would become damn easy with *any* library.
Just for the fuck of it, huh :) ?
I'm currently working on Java project part-time (it shows :) ), a small
app-specific server that has to pipeline work items back and forth and
do it fast. It's damn frustrating to see (and adapt) when your colleges
add/remove exception specs of their interface. And even I discover that
methods get or lose throws ExceptionX frequently during development.
Post by Marco Leise
Interesting read, do you know an example, probably a standard library of some other language,
that offers standard exceptions like this?

No, but it's about time to innovate. What I know for sure is that other
failed to deliver. (C++ STL - failed, Java - see above, .NET appears to
be in the same boat - i.e. more exceptions good and any)
Post by Marco Leise
If I understand you correctly, the idea is to not use
distinct error ids (be they codes or exception classes), but a mix of
flags.

While I think flags would be a very common way to _hint_ on how to
correct the error (or current state of system as a whole). I do suspect
that some error types may need more then just a flag, but who knows.

So you'd have pretty much one "catch" with a few "if"s,
but with the flags you can decide on the granularity.

Something like that. With ifs you query important properties about error
that allow you to pick the best recover decision. The whole propose of
flags is to unify only _important_ items for the _decision_ process and
hide useless variability (it still goes to message).

A comic-book example:
Top scientist breaks into Mr. President room and cries:
"We are doomed! Crystal oculator was miscalculated, all readings are
under 0.6543124312, helerium core just melted!"

Now does it mean it's time to evacuate promptly or instead give order to
isolate the secret lab and "end" this guy's project? Unless Mr.President
has a nice and long lookup table of all (pseudo) scientific terms he has
no clue.
--
Olshansky Dmitry
Marco Leise
2012-08-17 10:10:22 UTC
Permalink
Am Fri, 17 Aug 2012 12:48:01 +0400
Post by Dmitry Olshansky
[?]
Something like that. With ifs you query important properties about error
that allow you to pick the best recover decision. The whole propose of
flags is to unify only _important_ items for the _decision_ process and
hide useless variability (it still goes to message).
"We are doomed! Crystal oculator was miscalculated, all readings are
under 0.6543124312, helerium core just melted!"
Now does it mean it's time to evacuate promptly or instead give order to
isolate the secret lab and "end" this guy's project? Unless Mr.President
has a nice and long lookup table of all (pseudo) scientific terms he has
no clue.
I experimented with an enum that combines some error codes into a common action, like "retry later", "authenticate", "give up", the list goes on. But for the networking case (and that's why I was asking for an example of a broader application) it is actually nice to have exceptions that read like an FAQ: Am I still connected? Is the error temporary?

Now to the president: He might have a technical advisor, that translates the scientists explanation. Assume I'd implement an NNTP client. The protocol allows for sudden state changes by the server, that would cause unexpected errors. (Reconfiguration of a running server.) It also allows for extensions, that aside from some formalities can add functionality and error codes.
The president would be a programmer who is only interested in simple way to read or post in newsgroups with the library hiding all the minutiae of different error codes, detecting supported protocol versions or restoring state on a reconnect (like which newsgroup was selected).
The technical advisor may want to make use of an extension for authentication or searching the newsgroup, so he needs to get at all the information and also create raw requests with custom error handling. I think WebDAV is one such extension of HTTP.
--
Marco
Dmitry Olshansky
2012-08-17 10:33:52 UTC
Permalink
Post by Marco Leise
Am Fri, 17 Aug 2012 12:48:01 +0400
Post by Dmitry Olshansky
[?]
Something like that. With ifs you query important properties about error
that allow you to pick the best recover decision. The whole propose of
flags is to unify only _important_ items for the _decision_ process and
hide useless variability (it still goes to message).
"We are doomed! Crystal oculator was miscalculated, all readings are
under 0.6543124312, helerium core just melted!"
Now does it mean it's time to evacuate promptly or instead give order to
isolate the secret lab and "end" this guy's project? Unless Mr.President
has a nice and long lookup table of all (pseudo) scientific terms he has
no clue.
I experimented with an enum that combines some error codes into a common action, like "retry later", "authenticate", "give up", the list goes on.
Don't try to combine the _action_ into Exception. If there is definitive
action encoded (like re-try) then just do it. If there is uncertainty of
what exactly needs to be re-done (failure somewhere deeply nested) then
provide a hint on "state/possible recovery".
But for the networking case (and that's why I was asking for an example
of a broader application)
it is actually nice to have exceptions that read like an FAQ: Am I still
connected? Is the error temporary?

That's what I have shown (see transient & lost). Again exception
doesn't know _how_ (no action or it would do it itself) to restore, it
just provides nice hints to decision maker that indeed read as FAQ.
Post by Marco Leise
Now to the president: He might have a technical advisor, that translates the scientists explanation.
Assume I'd implement an NNTP client.
The protocol allows for sudden state changes by the server, that would
cause unexpected errors.
(Reconfiguration of a running server.)
Should be part of library's job (technicians of said scientist).

It also allows for extensions,
that aside from some formalities can add functionality and error codes.
Post by Marco Leise
The president would be a programmer who is only interested in simple way to read or post in newsgroups with the library hiding all the minutiae of different error codes, detecting supported protocol versions or restoring state on a reconnect (like which newsgroup was selected).
The technical advisor may want to make use of an extension for authentication or searching the newsgroup,
so he needs to get at all the information and also create raw requests
with custom error handling.
First of all there are convenience wrappers and there is low-level API.
My proposition doesn't hurt this separation at all.

And here is where inheritance finally plays it's role.
The thing does nest:
NNTPException : NetworkExcpetion (lost, transient)

NNTPException contains more interesting & specific info (maybe even
straight error codes - why not if they are well documented?)

So high-level API is constructed from low-level, and it has some simple
hardwired logic but by the end of day it just passes through most
unexpected exceptions.

So president type of programmer just looks at it as NetworkException
(as he should, the higher you sit the less you see) and checks easy
flags/fields to make global recovery action.

The technical savvy type of programmer can use low-level API directly
and even catch NNTP exceptions if RFC error codes are needed (and there
could be genuine Network errors like connection lost).
Post by Marco Leise
I think WebDAV is one such extension of HTTP.
I way incompetent with NNTP. So take the above with a grain of salt.

P.S. For some reason Thunderbird flats your paragraphs into a very long
lines, split by hand sorry if inaccurate ;).
--
Olshansky Dmitry
foobar
2012-08-19 07:04:46 UTC
Permalink
On Wednesday, 15 August 2012 at 19:29:44 UTC, Dmitry Olshansky
Post by Dmitry Olshansky
Post by Marco Leise
Am Sun, 12 Aug 2012 05:02:25 +0200
Post by Marco Leise
---D->>
/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* response = receives the whole response
* UnexpectedResponseException if the server sent us
garbage
*
* UnauthenticatedException we need retry after we have
logged in
*
* SecurityException we have to switch to a secure
connection for this
*
* DisconnectException the connection was unexpectedly
terminated
* Returns: the associated response code
*/
int foo(out string response)
{...}
<<-D---
---D->>
/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* response = receives the whole response
* Returns: the associated response code
*/
int foo(out string response) throws
UnexpectedResponseException, /// if the server sent us
garbage
UnauthenticatedException, /// we need retry after we have
logged in
SecurityException, /// we have to switch to a secure
connection for this
DisconnectException /// the connection was unexpectedly
terminated
{...}
<<-D--
When I see code like this I have one single thought - error
codes!
Indeed that's what is used in this example, with Exceptions
only being convenient (and separate) transport for error codes.
So the net progress is creating 1:1 type for each error
condition (start counting the lines of code) and then...
If I got it right somewhere later foo is supposed to be used
try{
...some_code
foo();
...other code
}
catch(UnexpectedResponseException)
{
print error and disconnect this server or retry?
}
catch(UnauthenticatedException)
{
print error 2
}
catch(SecurityException)
{
sslFoo(resp); // this one is just awesome ;)
}
catch(DisconnectException )
{
print error 3 & (ask to) reconnect?
}
Or used a catch all and do type switch Java-style to see if
Exception is one of interesting to you types. Needless to say
awful again.
First SecurityError is gross fiction as you either know to
authenticate (need credentials in the interface) or do auto
detection (like try HTTPS, then fallback to HTTP).
Moreover encoding _cause_ of error in type is useless, end user
needs a hint on the proper way to handle error. It's like
pointing out a guy who made the mistake and how stupid it is
instead of proposing the ways to fix the situation.
class NetworkException
{
@property bool transient; // packet lost or whatever, can
re-try with the same parameters
@property bool lost; // need reconnect to restore, server down
or disconnected or unexpected input
@property string msg();
}
catch(NetworkException ne)
{
if(ne.lost)
//ya kill me, but you got the idea ;)
goto ReconnectAndRetry;
if(ne.transient){
warning("..."); //log failure
goto RetryAndCheckTryCount;
}
error(ne.msg);
}
Including but not limited to the time when foo's author adds
more types to his "throws list". Unlike checked exceptions it
won't break build just for the fuck of it *and* it will still
work correctly.
In fact if we manage to come up with proper reasonable standard
exceptions like Network/IO/etc. that everybody derives from
error handling would become damn easy with *any* library.
IMHO, the suggested NetworkException is a bad design as it
weakens one of the goals of exceptions - to document the
programmer's intent.

This design basically wraps a bunch of flags in a class, adds
redundant boilerplate and reduces exceptions to glorified C-style
error codes and flags. What's the point of using exceptions here
at all if you use if statements and gotos to handle the error
anyway? Seems redundant to me. Moreover, I don't agree with
encoding the action or even just a hint in the exception. It goes
against common logic - the code that generates the error *cannot*
know what should be done to handle it - after all if it does know
it would handle the error itself. How would you even know to
define if a networking error is transient or not?

I agree that there are some issues with the common java style
design of exceptions. That does not mean we need to go back to
ifs and gotos. instead, we need to see how we can improve and
refine further the already quite successful exceptions design.
One improvement that can be done is something like Nemerle's -
Nemerle has pattern matching in the language and the catch clause
uses that same mechanism.
Dmitry Olshansky
2012-08-19 10:01:54 UTC
Permalink
Post by Dmitry Olshansky
Post by Marco Leise
Am Sun, 12 Aug 2012 05:02:25 +0200
Post by Marco Leise
---D->>
/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* response = receives the whole response
* UnexpectedResponseException if the server sent us garbage
*
* UnauthenticatedException we need retry after we have logged in
*
* SecurityException we have to switch to a secure connection for this
*
* DisconnectException the connection was unexpectedly terminated
* Returns: the associated response code
*/
int foo(out string response)
{...}
<<-D---
---D->>
/**
* Receives a response from the server.
*
* Some explanation of what
* the function does in detail.
*
* response = receives the whole response
* Returns: the associated response code
*/
int foo(out string response) throws
UnexpectedResponseException, /// if the server sent us garbage
UnauthenticatedException, /// we need retry after we have logged in
SecurityException, /// we have to switch to a secure connection for this
DisconnectException /// the connection was unexpectedly terminated
{...}
<<-D--
When I see code like this I have one single thought - error codes!
Indeed that's what is used in this example, with Exceptions only being
convenient (and separate) transport for error codes. So the net
progress is creating 1:1 type for each error condition (start counting
the lines of code) and then...
try{
...some_code
foo();
...other code
}
catch(UnexpectedResponseException)
{
print error and disconnect this server or retry?
}
catch(UnauthenticatedException)
{
print error 2
}
catch(SecurityException)
{
sslFoo(resp); // this one is just awesome ;)
}
catch(DisconnectException )
{
print error 3 & (ask to) reconnect?
}
Or used a catch all and do type switch Java-style to see if Exception
is one of interesting to you types. Needless to say awful again.
First SecurityError is gross fiction as you either know to
authenticate (need credentials in the interface) or do auto detection
(like try HTTPS, then fallback to HTTP).
Moreover encoding _cause_ of error in type is useless, end user needs
a hint on the proper way to handle error. It's like pointing out a guy
who made the mistake and how stupid it is instead of proposing the
ways to fix the situation.
class NetworkException
{
@property bool transient; // packet lost or whatever, can re-try
with the same parameters
@property bool lost; // need reconnect to restore, server down or
disconnected or unexpected input
@property string msg();
}
catch(NetworkException ne)
{
if(ne.lost)
//ya kill me, but you got the idea ;)
goto ReconnectAndRetry;
if(ne.transient){
warning("..."); //log failure
goto RetryAndCheckTryCount;
}
error(ne.msg);
}
Including but not limited to the time when foo's author adds more
types to his "throws list". Unlike checked exceptions it won't break
build just for the fuck of it *and* it will still work correctly.
In fact if we manage to come up with proper reasonable standard
exceptions like Network/IO/etc. that everybody derives from error
handling would become damn easy with *any* library.
IMHO, the suggested NetworkException is a bad design as it weakens one
of the goals of exceptions - to document the programmer's intent.
I've never seen this goal. "document programmer intent?" could you
expand on this?
This design basically wraps a bunch of flags in a class,
This example does show how common flags could be more useful then a
bunch of names. Flags in fact are fast way to do sets...

adds redundant
boilerplate
Compared to what?

and reduces exceptions to glorified C-style error codes and
flags.
They are already glorified C-style error codes in Java. And that's
something I tried to improve on (see the thread) Obviously the "and
flags" is all I get ;)

What's the point of using exceptions here at all if you use if
statements and gotos to handle the error anyway?
These goto's are conceptual, I don't write the entrie program for the
sake of brevity. And the point of exceptions is not to use rows of catch
instead of rows of ifs.

Seems redundant to me.
Moreover, I don't agree with encoding the action or even just a hint in
the exception. It goes against common logic - the code that generates
the error *cannot* know what should be done to handle it - after all if
it does know it would handle the error itself.
Right it doesn't know the cure-it-all action or it would have done it on
its own, I told so in my posts. Also keep in mind that low-level code
may know how to handle errors but doing it is not always acceptable or
possible at this level. So it passes info up
to somewhere above to take the correct action _based_ on information.

How would you even know
to define if a networking error is transient or not?
If it's worth retying later.
It's a question of can or cannot be. Hetwork unreachable is pretty much
not transient. Resolving host failed - can be a minor glitch.
The fact that connection broke also useful to know, so that handler can
safe reconnect (if handler thinks it feasible, low-level code can't
decide here).
I agree that there are some issues with the common java style design of
exceptions. That does not mean we need to go back to ifs and gotos.
First stop riding on gotos, they are long and tired concept. And my
design doesn't implies using them, I've put them in example as
conceptual handlers instead of writing ...handle condition x...

ifs in no way worse then catches on their own. The whole point was -
doing tons of error classes is indeed spawning glorified error codes.
Bundling together names is in no way better then proving a flag that
indicates these bundles.
instead, we need to see how we can improve and refine further the
already quite successful exceptions design.
I'd suggest that. In case you missed the design doesn't change a thing
in language. It even doesn't change the code that already works. It
suggest a specific standard hierarchy that helps unifying error
handling. That is something to discover.
One improvement that can be done is something like Nemerle's - Nemerle
has pattern matching in the language and the catch clause uses that same
mechanism.
So you match any of 10 exceptions in one catch. How do you work with
resulting object? Examples please.
What benefit it has compared to matching 1 sub-root exception type with
the field that indicates common information of interest of these 10
exceptions.
--
Olshansky Dmitry
Jonathan M Davis
2012-08-12 04:00:51 UTC
Permalink
Post by Marco Leise
I know that the Java way isn't perfect, because some lazy people write dummy
exception handlers to silence the errors, but its a worse solution to _not_
notify the user of a function, that it potentially throws exceptions, I
think. So I wish D was explicit about thrown Exceptions.
It's becoming more universally accepted that while checked exceptions seem
like a great idea up front, they're ultimately a bad idea:

http://www.artima.com/intv/handcuffs.html

D has nothrow, which statically verifies that _no_ exceptions are thrown, but
that's as close as it's going to get to having checked exceptions.

- Jonathan M Davis
Timon Gehr
2012-08-12 04:13:45 UTC
Permalink
Post by Marco Leise
---Java->>
class MyException extends Exception {
public MyException(String msg) {
super(msg);
}
public MyException(String msg, Throwable next) {
super(msg, next)
}
}
<<-Java---
---D->>
class MyException : Exception {
this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
super(msg, file, line, next);
}
this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) {
super(msg, file, line, next);
}
}
<<-D---
---D->>
class MyException : Exception {
mixin GenericExceptionConstructors;
}
<<-D---
Post by Marco Leise
---Java->>
void foo() throws MyException {
throw new MyException("test");
}
<<-Java---
This way you, as the author, are always aware of which Exceptions you handle inside the function and which may escape. This escalates to callers of the function as well, so that in the end the public API has an exact list of possibly thrown exceptions that the user must handle.
I have around a dozen different exceptions in a hierarchy and a few nested function calls. It's a maintenance horror to keep the DDoc up to date about thrown Exceptions. It also doesn't check the spelling or offers a link to the Exceptions.
...
I know that the Java way isn't perfect, because some lazy people write dummy exception handlers to silence the errors,
but its a worse solution to _not_ notify the user of a function, that it potentially throws exceptions, I think. So I
wish D was explicit about thrown Exceptions.
Well, any scheme for explicit exception annotations I can think of
is either unsound or impractical. What would you suggest concretely?
Walter Bright
2012-08-12 04:27:43 UTC
Permalink
Post by Marco Leise
I know that the Java way isn't perfect, because some lazy people write dummy
exception handlers to silence the errors, but its a worse solution to _not_
notify the user of a function, that it potentially throws exceptions, I
think. So I wish D was explicit about thrown Exceptions.
Read this and see if you change your mind:

http://www.mindview.net/Etc/Discussions/CheckedExceptions/

Anyhow, that article is why D does not have exception specifications. Also,
please note that C++ dropped exception specifications.
Jonathan M Davis
2012-08-12 04:36:07 UTC
Permalink
Post by Walter Bright
Anyhow, that article is why D does not have exception specifications. Also,
please note that C++ dropped exception specifications.
Though it should be noted that exception specifications are _far_ worse than
checked exceptions, because they're checked at runtime instead of compile
time, and they kill your program if they fail. So, instead of all of the
problems that you get with checked exceptions, you get your program killed at
runtime when you don't get your code quite right.

I think that you're going to have a hard time finding _anyone_ who actually
understands what C++'s exception specifications do and still thinks that
they're a good idea, whereas you _will_ find people who fully understand
checked exceptions and still think that they're a good idea.

- Jonathan M Davis
SomeDude
2012-08-15 17:00:19 UTC
Permalink
Post by Jonathan M Davis
Post by Walter Bright
Anyhow, that article is why D does not have exception
specifications. Also,
please note that C++ dropped exception specifications.
Though it should be noted that exception specifications are
_far_ worse than
checked exceptions, because they're checked at runtime instead
of compile
time, and they kill your program if they fail. So, instead of
all of the
problems that you get with checked exceptions, you get your
program killed at
runtime when you don't get your code quite right.
I think that you're going to have a hard time finding _anyone_
who actually
understands what C++'s exception specifications do and still
thinks that
they're a good idea, whereas you _will_ find people who fully
understand
checked exceptions and still think that they're a good idea.
- Jonathan M Davis
Yup, I'm among them, and I believe the only problem with checked
exceptions is that most people don't understand how to use them.
The main problem people have with checked exceptions is that they
are forced to handle error/exceptional cases, and therefore to
think about them. To my knowledge, there are no absolute rules or
truths in error handling, and therefore it seems it throws people
in all sorts of perplexity, because when it's not specified,
programmers have no idea who is supposed to handle an exception,
and how. So the worst offenders wipe them under the rug, which is
borderline criminal, because it can make it nearly impossible to
find the root cause of some runtime errors. Exceptions are
integral part of an interface, not an afterthought, i.e they are
part of an API design. But most average programmers never think
about error handling until the program explodes in their face (or
that of the customer). When one thinks about error handling
upfront, checked exceptions aren't a problem, they are a very
useful tool, and I use them all the time.
Marco Leise
2012-08-12 06:22:12 UTC
Permalink
I read both articles and while Bruce Eckel's text read a bit like repeated "swallow exception" to "avoid reams of code" I found the interview insightful. Both aren't entirely negative on checked exceptions and Hejlsberg actually wants them:

[Anders Hejlsberg]: "And so, when you take all of these issues, to me it just seems more thinking is needed before we put some kind of checked exceptions mechanism in place for C#. But that said, there's certainly tremendous value in knowing what exceptions can get thrown, and having some sort of tool that checks."

The arguments against the Java model were:

1) The programmer, annoyed by the compiler, swallows exceptions with empty catch clauses and forgets about them.

I think _if_ the intention is to get back to it later, you can always write "// TODO: handle the missing icon, by loading a stock icon from the OS". On the other hand, if you just don't want to declare the thrown exception, then this is wrong thinking in my opinion. Usually when I am in this situation in Java it makes me think about the error handling: Can this error be handled gracefully? If so, right here or should it bubble up? Where is the right level to handle it? At the end of this process I have the positive feeling that I got the error handling right.

2) Versioning; a change in a function may add a thrown exception, breaking client code. Often those clients want to ignore any exceptions (and pass them on).

Ok, accepted. The client code would have to change its "throws" list for no perceivable benefit.

3) Declaring the thrown exception doesn't scale well when you call into multiple subsystems that each throw different exceptions, where you would end up declaring dozens of thrown exceptions.

I frankly have to say that I never worked on that big projects, that I had to declare 40 thrown exceptions. And the question may be asked if you should just wrap the exception into another at that point, because you obviously reached a granularity at which the finer details of the sub system failures have become secondary.


So does it all boil down to the potentially long and cascading list of "throws"?
This is not a first grade language issue, I realize that. It's just when you come across it and find yourself documenting the thrown exceptions in DDoc where they aren't checked or anything, it cries for a solution. The solution current languages take seems to be "let the exceptions slip through, more often than not you don't handle them anyway".
How can we reap all the benefits, but avoid the manual listing of all thrown exceptions?
--
Marco
Paulo Pinto
2012-08-12 09:24:03 UTC
Permalink
Post by Marco Leise
[Anders Hejlsberg]: "And so, when you take all of these issues, to me it just seems more thinking is needed before we put some kind of checked exceptions mechanism in place for C#. But that said, there's certainly tremendous value in knowing what exceptions can get thrown, and having some sort of tool that checks."
1) The programmer, annoyed by the compiler, swallows exceptions with empty catch clauses and forgets about them.
I think _if_ the intention is to get back to it later, you can always write "// TODO: handle the missing icon, by loading a stock icon from the OS". On the other hand, if you just don't want to declare the thrown exception, then this is wrong thinking in my opinion. Usually when I am in this situation in Java it makes me think about the error handling: Can this error be handled gracefully? If so, right here or should it bubble up? Where is the right level to handle it? At the end of this process I have the positive feeling that I got the error handling right.
2) Versioning; a change in a function may add a thrown exception, breaking client code. Often those clients want to ignore any exceptions (and pass them on).
Ok, accepted. The client code would have to change its "throws" list for no perceivable benefit.
3) Declaring the thrown exception doesn't scale well when you call into multiple subsystems that each throw different exceptions, where you would end up declaring dozens of thrown exceptions.
I frankly have to say that I never worked on that big projects, that I had to declare 40 thrown exceptions. And the question may be asked if you should just wrap the exception into another at that point, because you obviously reached a granularity at which the finer details of the sub system failures have become secondary.
So does it all boil down to the potentially long and cascading list of "throws"?
This is not a first grade language issue, I realize that. It's just when you come across it and find yourself documenting the thrown exceptions in DDoc where they aren't checked or anything, it cries for a solution. The solution current languages take seems to be "let the exceptions slip through, more often than not you don't handle them anyway".
How can we reap all the benefits, but avoid the manual listing of all thrown exceptions?
I have a large experience in JVM and to some extent .NET enterprise
projects. These projects have a multi-site component usually in three
development sites, scattered around Europe and Asia, having from 30
up to 300 developers on project.

To keep costs per team member low, not everyone on the projects is a top
coder, many are on their first or second big project.

Throws in Java method declarations are reduced to "throws Exception", or
"throws RuntimeException" with the real exception being wrapped in a
RuntimeException.

Many places where the exceptions occur are handled like

try {
// ...
} catch (Exception e) {
e.printStackException(); //TODO: fix later (Like you describe)
}

Sure we try to fight against this type of code, but at the end of the
day, there are many other issues to solve, and this type of code gets
low priority as fixing it does not make money.


Checked exceptions are a failed experiment, and I surely hope that D
does not get them.

It is a bit like the arguments about manual memory management, sure good
coders are able to make proper use of such abstractions, but in real
life, projects seldom have real good coders on their teams. And when
they have them, there are many other issues to take care of.

--
Paulo
bearophile
2012-08-12 11:48:22 UTC
Permalink
And when they have them, there are many other issues to take
care of.<
This is a bad argument because there are always other issues to
take care of. Even if a firm buys an artificial intelligence able
to self-write all the code, I am sure people in that firm will
keep saying "we have many other issues beside writing code!".

Bye,
bearophile
Paulo Pinto
2012-08-12 12:34:58 UTC
Permalink
Post by bearophile
And when they have them, there are many other issues to take
care of.<
This is a bad argument because there are always other issues to
take care of. Even if a firm buys an artificial intelligence
able to self-write all the code, I am sure people in that firm
will keep saying "we have many other issues beside writing
code!".
Bye,
bearophile
Sure it is, but I've learned that if you live in the Fortune 500
corporation world, it not worth fighting against it.

Actually there are a few talks in InfoQ about this type of issue.

http://www.infoq.com/presentations/Stop-Refactoring
http://www.infoq.com/presentations/Who-Ever-Said-Programs-Were-Supposed-to-be-Pretty

--
Paulo
Marco Leise
2012-08-13 02:46:02 UTC
Permalink
Am Sun, 12 Aug 2012 14:34:58 +0200
Post by Paulo Pinto
Post by bearophile
And when they have them, there are many other issues to take
care of.<
This is a bad argument because there are always other issues to
take care of. Even if a firm buys an artificial intelligence
able to self-write all the code, I am sure people in that firm
will keep saying "we have many other issues beside writing
code!".
Bye,
bearophile
Sure it is, but I've learned that if you live in the Fortune 500
corporation world, it not worth fighting against it.
Actually there are a few talks in InfoQ about this type of issue.
http://www.infoq.com/presentations/Stop-Refactoring
http://www.infoq.com/presentations/Who-Ever-Said-Programs-Were-Supposed-to-be-Pretty
--
Paulo
There is code to be written in different areas of business for sure. There is rapid changing business code as yours, there is moderately changing application software, there are extensible tools like Eclipse and it goes further on to libraries and software used in sensitive areas, nuclear power plants, medical or mars robots.
So there's definitely people who don't want to be slowed down by unnecessary errors and strictness in the language because it doesn't make money, and others who want to have a look at every little warning to make the code as failsafe as possible. I'm thinking of unhandled exceptions as well as integer overflow and similar.
In the end your products wouldn't be finished in time either, if the libraries and tools you build them with weren't good. And open-source software often has the benefit there, that you can actually look at the source if something remains unclear in the documentation, and also that often a lot of people have looked over the code; people with different backgrounds and experience, that detect other types of bugs and pitfalls.
In that way success is not always defined economically. It is defined by the goals you set for a given project. A person who uses test driven development might chose D, because of its built in unit tests and invariant() {...}. Similarly a physicist might shy away from it, because of the semantic noise. (They want an integer, but are confronted with the concept of signed/unsigned 32 and 64 bit values and BitInts)

TL:DR - it all depends on your target audience :)
--
Marco
Walter Bright
2012-08-12 23:54:07 UTC
Permalink
And when they have them, there are many other issues to take care of.<
This is a bad argument because there are always other issues to take care of.
Even if a firm buys an artificial intelligence able to self-write all the code,
I am sure people in that firm will keep saying "we have many other issues beside
writing code!".
Not at all. If you fail to focus on your priorities, you will fail.
SomeDude
2012-08-15 17:14:30 UTC
Permalink
Post by Marco Leise
I read both articles and while Bruce Eckel's text read a bit
like repeated "swallow exception" to "avoid reams of code" I
found the interview insightful. Both aren't entirely negative
[Anders Hejlsberg]: "And so, when you take all of these
issues, to me it just seems more thinking is needed before we
put some kind of checked exceptions mechanism in place for C#.
But that said, there's certainly tremendous value in knowing
what exceptions can get thrown, and having some sort of tool
that checks."
1) The programmer, annoyed by the compiler, swallows exceptions
with empty catch clauses and forgets about them.
I think _if_ the intention is to get back to it later, you can
always write "// TODO: handle the missing icon, by loading a
stock icon from the OS". On the other hand, if you just don't
want to declare the thrown exception, then this is wrong
thinking in my opinion. Usually when I am in this situation in
Java it makes me think about the error handling: Can this error
be handled gracefully? If so, right here or should it bubble
up? Where is the right level to handle it? At the end of this
process I have the positive feeling that I got the error
handling right.
2) Versioning; a change in a function may add a thrown
exception, breaking client code. Often those clients want to
ignore any exceptions (and pass them on).
Ok, accepted. The client code would have to change its "throws"
list for no perceivable benefit.
3) Declaring the thrown exception doesn't scale well when you
call into multiple subsystems that each throw different
exceptions, where you would end up declaring dozens of thrown
exceptions.
I frankly have to say that I never worked on that big projects,
that I had to declare 40 thrown exceptions. And the question
may be asked if you should just wrap the exception into another
at that point, because you obviously reached a granularity at
which the finer details of the sub system failures have become
secondary.
So does it all boil down to the potentially long and cascading
list of "throws"?
This is not a first grade language issue, I realize that. It's
just when you come across it and find yourself documenting the
thrown exceptions in DDoc where they aren't checked or
anything, it cries for a solution. The solution current
languages take seems to be "let the exceptions slip through,
more often than not you don't handle them anyway".
How can we reap all the benefits, but avoid the manual listing
of all thrown exceptions?
My experience and conclusions are very similar to yours. I would
simply add that the versioning problem simply is a change of
interface. The compiler will force you to add a throws clause to
your method signature if your code is likely to throw a new kind
of exception (unless you handle it yourself). So you very well
know you are making a breaking change, and that you risk
infuriating your customers because of this. So I find it to be a
fairly weak argument.
Adam D. Ruppe
2012-08-12 14:33:39 UTC
Permalink
Perhaps a workable compromise is to make ddoc able to
automatically output the throws list.

That way, we don't have the hassle of checks, but we do have a
maintained list at relatively no hassle.


If you call a function to which the source code isn't available,
ddoc can just point you toward that function's docs. It won't be
100% complete due to this, but I think it would be decent.
simendsjo
2012-08-12 15:19:01 UTC
Permalink
On Sun, 12 Aug 2012 16:33:39 +0200, Adam D. Ruppe
Perhaps a workable compromise is to make ddoc able to automatically
output the throws list.
That way, we don't have the hassle of checks, but we do have a
maintained list at relatively no hassle.
If you call a function to which the source code isn't available, ddoc
can just point you toward that function's docs. It won't be 100%
complete due to this, but I think it would be decent.
Once we get a lexer, parser and lint tool, undocumented parameters,
exceptions etc could be marked as warnings too.
Looking into the future here of course :)
Jonathan M Davis
2012-08-12 21:01:49 UTC
Permalink
Post by Adam D. Ruppe
Perhaps a workable compromise is to make ddoc able to
automatically output the throws list.
That way, we don't have the hassle of checks, but we do have a
maintained list at relatively no hassle.
That's both a good idea and bad idea, because all it's going to be able to
list is the exceptions thrown directly in the function. In order to list them
all, it would have to go digging through the whole call list (which not only
would be expensive, but isn't even necessarily possible if the source isn't
fully available), and if any classes are involved, then inheritence could
totally mess up the list, since different derived classes could throw different
exceptions.

So, unless all you care about is what's thrown directly from the function,
you'd end up with a very incomplete list. The advantage is that you'd at least
have a partial list, but if it gave the impression that it was the whole list,
then that would be a problem. Of course, writing it by hand also tends to only
list what gets thrown directly (or maybe also what's thrown in the direct
helpe functions), so there wouldn't necessarily be much difference. So, it may
be close to what would be written by hand. It's definitely true though that it
won't be listing all of the thrown exceptions in the general case.

- Jonathan M Davis
Regan Heath
2012-08-13 10:31:34 UTC
Permalink
On Sun, 12 Aug 2012 22:01:49 +0100, Jonathan M Davis <jmdavisProg at gmx.com>
Post by Jonathan M Davis
Post by Adam D. Ruppe
Perhaps a workable compromise is to make ddoc able to
automatically output the throws list.
That way, we don't have the hassle of checks, but we do have a
maintained list at relatively no hassle.
That's both a good idea and bad idea, because all it's going to be able
to
list is the exceptions thrown directly in the function. In order to list
them
all, it would have to go digging through the whole call list (which not
only
would be expensive, but isn't even necessarily possible if the source
isn't
fully available), and if any classes are involved, then inheritence could
totally mess up the list, since different derived classes could throw
different
exceptions.
So, unless all you care about is what's thrown directly from the
function,
you'd end up with a very incomplete list. The advantage is that you'd at
least
have a partial list, but if it gave the impression that it was the whole
list,
then that would be a problem. Of course, writing it by hand also tends
to only
list what gets thrown directly (or maybe also what's thrown in the direct
helpe functions), so there wouldn't necessarily be much difference. So,
it may
be close to what would be written by hand. It's definitely true though
that it
won't be listing all of the thrown exceptions in the general case.
I wonder if it might be possible to make an intellisense style GUI/IDE
tool/plugin which could determine all exceptions thrown either by direct
code inspection or ddoc inspection (when source is unavailable) such that
it could actually build a complete list. It would need to cache results
in order to be anywhere near performant I reckon.

R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/
Jacob Carlborg
2012-08-13 13:45:03 UTC
Permalink
Post by Regan Heath
I wonder if it might be possible to make an intellisense style GUI/IDE
tool/plugin which could determine all exceptions thrown either by direct
code inspection or ddoc inspection (when source is unavailable) such
that it could actually build a complete list. It would need to cache
results in order to be anywhere near performant I reckon.
I think that would be great and possible, at lest to some extent.
There's always problems with unknown subclasses, dynamically loaded code
and so on.
--
/Jacob Carlborg
Nathan M. Swan
2012-08-13 08:00:31 UTC
Permalink
Post by Marco Leise
I just got a bit frustrated and wanted to say that I like
working with Exceptions in Java a lot more.
I don't. When writing a simple command line program, when there's
an error, it usually means the user messed up and I can't
recover. I just print the message and terminate. I don't want to
have to write "throws Exception" for everything.

void main(string[] args) {
try {
realMain(args);
return 0;
} catch (Exception e) {
stderr.writeln(e.msg);
}
}

The idea sounds nice, but it's annoying in practice. The point of
exceptions is to _centralize_ error handling. Being forced to
either catch or declare is almost as bad as C-style errno error
handling.

Perhaps an annotation might be nice, as long as it doesn't force
catching:

void buggyFunction(string file, int exception)
@throws(StdioException);
Marco Leise
2012-08-13 08:50:47 UTC
Permalink
Am Mon, 13 Aug 2012 10:00:31 +0200
Post by Nathan M. Swan
Post by Marco Leise
I just got a bit frustrated and wanted to say that I like
working with Exceptions in Java a lot more.
I don't. When writing a simple command line program, when there's
an error, it usually means the user messed up and I can't
recover. I just print the message and terminate. I don't want to
have to write "throws Exception" for everything.
But now and then you probably write something else than a simple command line program, right?
Post by Nathan M. Swan
The idea sounds nice, but it's annoying in practice. The point of
exceptions is to _centralize_ error handling. Being forced to
either catch or declare is almost as bad as C-style errno error
handling.
Ok, it is annoying. Still exceptions are not just an opaque blob with a stack trace that you catch in your main(). They are more flexible, but it is difficult to efficiently use this flexibility. For example a routine that processes multiple files may continue with the next file on FileNotFoundExceptions, but throw others. A network application may attempt a few times to reconnect in case of a disconnect. That cannot be accomplished by one centralized exception handler. There are also a couple of occasions where a ConvException is better catched and rethrown as a BadServerResponseException (e.g. a number was expected, but something else returned).
In particular when writing a library I want to offer proper exceptions, and since they have the bad nature of aborting all your nested function calls, it is important for me to have the thrown exceptions properly declared - a task that the compiler can do better and faster than me.
As it stands I might diverge from my old practice and use only one exception type for a library.
Post by Nathan M. Swan
Perhaps an annotation might be nice, as long as it doesn't force
void buggyFunction(string file, int exception)
@throws(StdioException);
As someone else said, a D lint tool might help with warnings about undocumented thrown exceptions. But personally I want to be "annoyed" to death by the language until I have made it clear what I want to do with the exceptions, even if I just write "catch (ConvException) { /* cannot happen, input is known good */ }".

I don't know how many there are who think like me. Your @throws proposal for example could be used to tell the compiler that I want Java style checked exceptions for this function and have the compiler check that I listed them all. An empty list would actually be 'nothrow' then.
--
Marco
Marco Leise
2012-08-13 09:08:28 UTC
Permalink
Am Mon, 13 Aug 2012 10:50:47 +0200
It's actually funny if you consider following 2 sentences from http://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html :

"Any Exception that can be thrown by a method is part of the method's public programming interface. Those who call a method must know about the exceptions that a method can throw so that they can decide what to do about them."

So it is the _public_ programming interface, we should be worried about, not forcing "throws"-lists onto every function and method that isn't intended to be called by others, which would neatly work with an optional @throws.
--
Marco
Marco Leise
2012-08-13 10:01:58 UTC
Permalink
I think I should clarify some things up front.

o First of all, to be able to add @throws to a function at any point,
means that the compiler would internally replace the flag 'isnothrow'
with a list of thrown exceptions. That is not much different from what
there is now and would be part of the mangled name as well, if
acceptable (length restraints), or - since you cannot overload by
exceptions - part of an extension to the object file.

o nothrow would be equivalent to the empty list @throws() and usable
interchangeably.

o For protected and public methods of objects and interfaces that are
not declared as nothrow, the thrown exceptions would inferred as
@throws(Exception). This has to be done, since an override of these
methods could introduce any exception type.

o private methods and regular functions are properly inferred with the
exceptions they actually throw.

o At this point all the functions and methods carry their hidden @throws
specification around, but it is neither visible in source code, nor is
there any impact for developers.

o Once someone adds an @throws to a function, the compiler compares it to
its internal list and complains if it is missing an exception type.
The explicit list can contain additional exceptions that aren't in the
compiler inferred list and it can offer more general exceptions, like
the base type Exception, as a catch-all.

o What happens with object methods that don't declare anything?
Since they are inferred as @throws(Exception), no narrowing is possible.
On the other hand, I think this is the only natural way to handle it.

Thoughts ?
--
Marco
Nathan M. Swan
2012-08-13 18:17:12 UTC
Permalink
Post by Marco Leise
Thoughts ?
I like this idea - you can use checked exceptions, but you aren't
forced.

Though I think private and free functions should by default just
use @throws(Exception). Not using @throws is like saying "I don't
pay attention to what errors might occur, a new version might be
different."

NMS
Marco Leise
2012-08-13 18:52:24 UTC
Permalink
Am Mon, 13 Aug 2012 20:17:12 +0200
Post by Nathan M. Swan
Post by Marco Leise
Thoughts ?
I like this idea - you can use checked exceptions, but you aren't
forced.
Though I think private and free functions should by default just
pay attention to what errors might occur, a new version might be
different."
NMS
Ok, that would simplify the concept, but what would you do about templated functions that operate on ranges, like Dimitry presented? I'd rather have the compiler deduce the exceptions to @throws() where I passed in a simple int[], than have to deal with @throws(Exception). For normal functions it may be feasible.
Also all existing code is not annotated, leaving us in the same situation as with other missing attributes in Phobos and people start to complain "I'd like to use @throws, but everything is just @throws(Exception)" :p
--
Marco
SomeDude
2012-08-15 17:44:13 UTC
Permalink
Post by Nathan M. Swan
Post by Marco Leise
I just got a bit frustrated and wanted to say that I like
working with Exceptions in Java a lot more.
I don't. When writing a simple command line program, when
there's an error, it usually means the user messed up and I
can't recover. I just print the message and terminate. I don't
want to have to write "throws Exception" for everything.
void main(string[] args) {
try {
realMain(args);
return 0;
} catch (Exception e) {
stderr.writeln(e.msg);
}
}
The idea sounds nice, but it's annoying in practice. The point
of exceptions is to _centralize_ error handling. Being forced
to either catch or declare is almost as bad as C-style errno
error handling.
Or you could have told him HOW he messed up. By just throwing an
Exception, you are not helpful at all, you are just telling that
he is an ass and that he will be punished for that. Or maybe he
will curse you thinking that your program doesn't work.

Let's say I write a FileParser class which will read a table of
doubles in a file. I use a number of methods defined in the JDK
like say Double.parse(String) which throws a
NumberFormatException("on line" + lineNumber), and catching that
exception, I am able to tell the user that one of the numbers on
line lineNumber is badly formatted, instead of just telling him
that reading the file with 200,000 lines crashed the program. If
I catch an IOException as well, I can inform him that the file is
unreadable instead (but that the formatting was not yet in
cause). But if I catch a FileNotFoundException first (which
derives from IOException), I can be more helpful by informing
him/her that the filename passed to the program is wrong.
These are entirely different classes of errors, so they need to
be distinguished if they are to be handled differently, which is
not possible with Exception.
Post by Nathan M. Swan
Perhaps an annotation might be nice, as long as it doesn't
void buggyFunction(string file, int exception)
@throws(StdioException);
It's like saying adding typing to the language is nice as long as
it's not enforced by the compiler.
Kagamin
2012-08-16 10:36:59 UTC
Permalink
Post by SomeDude
Or you could have told him HOW he messed up. By just throwing
an Exception, you are not helpful at all, you are just telling
that he is an ass and that he will be punished for that. Or
maybe he will curse you thinking that your program doesn't work.
Let's say I write a FileParser class which will read a table
of doubles in a file. I use a number of methods defined in the
JDK like say Double.parse(String) which throws a
NumberFormatException("on line" + lineNumber), and catching
that exception, I am able to tell the user that one of the
numbers on line lineNumber is badly formatted, instead of just
telling him that reading the file with 200,000 lines crashed
the program.
How checked exceptions would help you decide whether line number
should be included in the error message?
Post by SomeDude
If I catch an IOException as well, I can inform him that the
file is unreadable instead (but that the formatting was not yet
in cause). But if I catch a FileNotFoundException first (which
derives from IOException), I can be more helpful by informing
him/her that the filename passed to the program is wrong.
How checked exceptions would help you design exception hierarchy?
What's your point?
SomeDude
2012-08-18 02:51:09 UTC
Permalink
Post by Kagamin
Post by SomeDude
Or you could have told him HOW he messed up. By just throwing
an Exception, you are not helpful at all, you are just telling
that he is an ass and that he will be punished for that. Or
maybe he will curse you thinking that your program doesn't
work.
Let's say I write a FileParser class which will read a table
of doubles in a file. I use a number of methods defined in the
JDK like say Double.parse(String) which throws a
NumberFormatException("on line" + lineNumber), and catching
that exception, I am able to tell the user that one of the
numbers on line lineNumber is badly formatted, instead of just
telling him that reading the file with 200,000 lines crashed
the program.
How checked exceptions would help you decide whether line
number should be included in the error message?
You didn't get my point because it was badly formulated. I wanted
to emphasize here that because the exception is typed, you can
give a better information on what is wrong (here a bad
formatting, and you throw in the line number). The interface of
Double.parse() *forces* you to catch that exception, and that is
a *good* thing. If it didn't force you to catch it, you would
lazily 1) not catch and let the program crash 2) catch an
Exception and not be able to tell the user what's the root cause
of the error, leaving him scratching his head and thinking that
your program is actually at fault (because it failed) and not the
file.
So in a sense, Double.parse(), by throwing a
NumberFormatException, forces you to be precise in your error
handling and to think about it, i.e answer the questions:
a) must I handle the exception or pass it to another level where
it makes sense to handle it
b) how can I provide the best information as to how to handle the
situation.

Just placing a catch all Exception at the top level is obviously
not enough, because all it says is "something went wrong". It
doesn't help handling the error at the right place, in the
correct manner.

It's a misconception that is all too common in the C++ world
because in C++, exceptions are so crippled that they are
basically unusable, so people resort to the catch all trick,
which is most often less than useless.
Post by Kagamin
Post by SomeDude
If I catch an IOException as well, I can inform him that the
file is unreadable instead (but that the formatting was not
yet in cause). But if I catch a FileNotFoundException first
(which derives from IOException), I can be more helpful by
informing him/her that the filename passed to the program is
wrong.
How checked exceptions would help you design exception
hierarchy? What's your point?
The exception hierarchy helps choosing the right level of
"graininess" in error handling depending on how you want the user
to handle errors. You may want to help him establish a diagnosis,
in which case you will throw the most discriminating exceptions,
or you may just want to inform him that something went wrong, in
which case you will rather throw a root exception class. This is
very similar to the choice you make when writing logs and choose
their log level. Because checked exceptions force you to catch
them, you can catch exceptions high in the hierarchy in order to
avoid catching every single leaf, which is the case if you are
not interested in handling all these different cases.
Marco Leise
2012-08-17 08:02:54 UTC
Permalink
Am Wed, 15 Aug 2012 19:44:13 +0200
Post by SomeDude
Post by Nathan M. Swan
Perhaps an annotation might be nice, as long as it doesn't
void buggyFunction(string file, int exception)
@throws(StdioException);
It's like saying adding typing to the language is nice as long as
it's not enforced by the compiler.
I am on the same boat, but the number of people who are annoyed by the excrescences is high. That's why I came to the conclusion that both of you should have their way. You can annotate (which means the buggyFunction can only throw StdioException), but you don't need to catch it on the caller side. (Unless you also add an @throws or nothrow there.)
Also, I want to repeat that @throws can be put into DDoc automatically, avoiding the manual documentation and resulting in more modern convenience.
--
Marco
Dmitry Olshansky
2012-08-13 15:32:38 UTC
Permalink
Post by Marco Leise
I just got a bit frustrated and wanted to say that I like working with Exceptions in Java a lot more.
---Java->>
class MyException extends Exception {
public MyException(String msg) {
super(msg);
}
public MyException(String msg, Throwable next) {
super(msg, next)
}
}
<<-Java---
I think the true cryptonite that melts "checked exceptions" to a pile of
green goo is templated code:

So (*yawn*) tell what kind of exception specification the following
function should have:

auto joiner(RoR, Separator)(RoR r, Separator sep);

How would you guarantee upfront what kind of exceptions it can throw is
beyond me. It all depends on code that you can't reach or know by the
very definition of template.

Back to Java: what is I find strange is the lack of sensible tools to do
transactional or exception safe code within the language. No RAII
objects or just at least any kludge to reliably register
cleanup/rollback, only "good" old try/finally.
--
Dmitry Olshansky
Timon Gehr
2012-08-13 15:50:38 UTC
Permalink
Post by Dmitry Olshansky
I think the true cryptonite that melts "checked exceptions" to a pile of
So (*yawn*) tell what kind of exception specification the following
auto joiner(RoR, Separator)(RoR r, Separator sep);
How would you guarantee upfront what kind of exceptions it can throw is
beyond me. It all depends on code that you can't reach or know by the
very definition of template.
Well, presumably the exception specification would be inferred
automatically for templates.
Dmitry Olshansky
2012-08-13 15:54:47 UTC
Permalink
Post by Timon Gehr
Post by Dmitry Olshansky
I think the true cryptonite that melts "checked exceptions" to a pile of
So (*yawn*) tell what kind of exception specification the following
auto joiner(RoR, Separator)(RoR r, Separator sep);
How would you guarantee upfront what kind of exceptions it can throw is
beyond me. It all depends on code that you can't reach or know by the
very definition of template.
Well, presumably the exception specification would be inferred
automatically for templates.
What's the propose then? And how end user will get any idea what to put
in his function where he uses it? I see one, but it's not pretty - run
compiler once - see complaints, add requested types to throws, re-run
the compiler?
--
Dmitry Olshansky
Timon Gehr
2012-08-13 16:09:27 UTC
Permalink
Post by Dmitry Olshansky
Post by Timon Gehr
Post by Dmitry Olshansky
I think the true cryptonite that melts "checked exceptions" to a pile of
So (*yawn*) tell what kind of exception specification the following
auto joiner(RoR, Separator)(RoR r, Separator sep);
How would you guarantee upfront what kind of exceptions it can throw is
beyond me. It all depends on code that you can't reach or know by the
very definition of template.
Well, presumably the exception specification would be inferred
automatically for templates.
What's the propose then?
The same as the OT describes. Don't get me wrong, I am certainly no
proponent of adding Java-style checked exceptions to D.
Post by Dmitry Olshansky
And how end user will get any idea what to put
in his function where he uses it?
The end user is aware of what the template arguments are.
Post by Dmitry Olshansky
I see one, but it's not pretty - run compiler once - see complaints,
add requested types to throws, re-run the compiler?
Either add requested types to throws or catch and handle the respective
exceptions. I assume that this is basically the entire point of
statically checked exception specifications.
Marco Leise
2012-08-13 18:39:47 UTC
Permalink
Am Mon, 13 Aug 2012 19:54:47 +0400
Post by Dmitry Olshansky
Post by Timon Gehr
Post by Dmitry Olshansky
I think the true cryptonite that melts "checked exceptions" to a pile of
So (*yawn*) tell what kind of exception specification the following
auto joiner(RoR, Separator)(RoR r, Separator sep);
How would you guarantee upfront what kind of exceptions it can throw is
beyond me. It all depends on code that you can't reach or know by the
very definition of template.
The good thing is whatever your argument is, nothrow inevitably has to suffer from it, too.
Really I'd like to label this situation as 'bad', when you don't know what exceptions a function may throw. Then why have different exception types at all?
Post by Dmitry Olshansky
Post by Timon Gehr
Well, presumably the exception specification would be inferred
automatically for templates.
What's the propose then? And how end user will get any idea what to put
in his function where he uses it? I see one, but it's not pretty - run
compiler once - see complaints, add requested types to throws, re-run
the compiler?
Yes it is that bad(tm) ^^. Ok, who is the end user? If I am the user of someone's library and it has an @throws(...) spec, I get an exact idea what to put into my code to handle the exceptions - if I want to.

If I'm writing library code on the other hand it depends on the business I'm doing. I'd expect that fast living close to the market code will likely not use this feature, whereas libraries want to be specific about their public API, which includes the exceptions that they throw. I see it as a final step alongside the documentation of a library to add the @throws(...), so it doesn't break the workflow much.

As for the benefits:

o can act as a contract in class hierarchies when used on overrideable methods
o allows you to decide at any point to limit the thrown exceptions to a certain set
(e.g. handle cases of ConvException or UTFException before the public API)
o makes this automatically visible in DDoc (including any comment on the exception)
@throws(ReadException /** if it is not correct. */);
o you don't have to document them manually and keep the DDoc up to date,
which is much more time consuming than running the compiler twice
o let the compiler work for you
o I am a lousy salesman writing walls of text


Take a look for example at the socket API. Some methods specify the thrown exceptions, others only hint at them by catching them in examples or don't document them at all. You have to start guessing or digging through the source code to find out which error situations result in what exceptions. The InternetAddress ctor taking a host name looks innocent for example as well as most of the Socket methods.

In std.datetime, there is a function TimeZone.getInstalledTZNames() where I wonder if the docs are correct. They say on Windows it throws a DateTimeException when the registry cannot be read, but around the actual registry function call I see no try catch to wrap the thrown exception.

std.format is sometimes throwing FormatException, but sometimes Exception on similar cases or an UTFException.
--
Marco
Mehrdad
2012-08-14 23:13:04 UTC
Permalink
Post by Dmitry Olshansky
So (*yawn*) tell what kind of exception specification the
auto joiner(RoR, Separator)(RoR r, Separator sep);
auto joiner(RoR, Separator)(RoR r, Separator sep)
throws(?);
Mehrdad
2012-08-14 23:21:29 UTC
Permalink
On Monday, 13 August 2012 at 15:32:45 UTC, Dmitry Olshansky
Post by Dmitry Olshansky
So (*yawn*) tell what kind of exception specification the
auto joiner(RoR, Separator)(RoR r, Separator sep);
auto joiner(RoR, Separator)(RoR r, Separator sep)
throws(?);
Or even better:

auto joiner(RoR, Separator)(RoR r, Separator sep)
throws(auto);


That way it's easy enough for the programmer to make the compiler
shut up (it's certainly easier than swallowing the exception),
while allowing him to write functions that are perfectly
transparent toward exceptions, and which would be allowed to
throw/catch as they would in any other exception-unchecked
language.


IMO it would work well in practice.
Mehrdad
2012-08-15 11:38:30 UTC
Permalink
Post by Dmitry Olshansky
auto joiner(RoR, Separator)(RoR r, Separator sep)
throws(auto);
That way it's easy enough for the programmer to make the
compiler shut up (it's certainly easier than swallowing the
exception), while allowing him to write functions that are
perfectly transparent toward exceptions, and which would be
allowed to throw/catch as they would in any other
exception-unchecked language.
IMO it would work well in practice.
Caveat perhaps worth mentioning:
Indirect calls (function pointers, delegates, virtual methods)
would be inferred as throw-all, and would preferably give an
informational warning to the user that they may need tighter
exception specifications.
Paulo Pinto
2012-08-15 06:44:44 UTC
Permalink
Post by Dmitry Olshansky
Back to Java: what is I find strange is the lack of sensible
tools to do transactional or exception safe code within the
language. No RAII objects or just at least any kludge to
reliably register cleanup/rollback, only "good" old try/finally.
Since Java 7, you have the same tools Haskell, Scheme, Lisp, C#
and Python already offer for such cases.

For example:


try (BufferedReader br =
new BufferedReader(new FileReader(path))) {
return br.readLine();
}

--
Paulo
Marco Leise
2012-08-17 09:45:47 UTC
Permalink
Am Mon, 13 Aug 2012 19:32:38 +0400
Post by Dmitry Olshansky
I think the true cryptonite that melts "checked exceptions" to a pile of
So (*yawn*) tell what kind of exception specification the following
auto joiner(RoR, Separator)(RoR r, Separator sep);
How would you guarantee upfront what kind of exceptions it can throw is
beyond me. It all depends on code that you can't reach or know by the
very definition of template.
At first I thought the very definition of template doesn't do much in terms of nothrow inference, so what. But I made it to easy on myself, so I'll go into details a bit.
When the template is instantiated and it has no @throws annotations, it would infer the thrown exceptions in the fashion nothrow is inferred.
If you add nothrow/@throws to a surrounding function that uses this instantiation, then you get into the situation that you really don't know upfront what is thrown until you do a test compile.

I could blame it on the compile time duck typing, that evades declared thrown exceptions. It seems that exceptions just don't work with templates [that call arbitrary functions passed to them through struct/class parameters]. In general it is not possible to reason about the thrown exceptions in templated code, unless everything is marked nothrow or the code is simple, like chains of filter, map, reduce, ?.
Currently you cannot use joiner in nothrow functions and I have no idea which exception you are supposed to catch.

This is a whole different topic and for the sake of this proposal I'd like to go with not documenting these exceptions introduced from the outside, but making them aware to the programmer whenever he/she uses @throws on functions using such templates.
--
Marco
Manipulator
2012-08-16 13:11:41 UTC
Permalink
I had times when I missed the Java-style Exception handling.

First time I just the D sockets exceptions was thrown during
runtime. The Library Reference didn't tell my anything about
exceptions neither did the compiler.
This could've been avoided with documentation stating which
exceptions can be thrown or if the compiler had warned me about
it.
I see checked exception handling as mean to remind programmers to
handle certain errors. This is not done in D.

I've also cursed the Java-style many times. For example:
interface MyInterface {
public void method throws IOException();
}

class MyClass implements MyInterface {
public void method throws IOException() {
//Doesn't need throw IOException!!!!
}
}

I liked the idea when the compiler is able to warn me about
unhandled exceptions.
Marco Leise
2012-08-17 08:07:41 UTC
Permalink
Am Thu, 16 Aug 2012 15:11:41 +0200
Post by Manipulator
I had times when I missed the Java-style Exception handling.
First time I just the D sockets exceptions was thrown during
runtime. The Library Reference didn't tell my anything about
exceptions neither did the compiler.
This could've been avoided with documentation stating which
exceptions can be thrown or if the compiler had warned me about
it.
I see checked exception handling as mean to remind programmers to
handle certain errors. This is not done in D.
interface MyInterface {
public void method throws IOException();
}
class MyClass implements MyInterface {
public void method throws IOException() {
//Doesn't need throw IOException!!!!
}
}
I liked the idea when the compiler is able to warn me about
unhandled exceptions.
I've been thinking shortly about this. Can we narrow down the thrown exceptions? Can an interface declare that it's descendants may throw IOExceptions, but an implementation may practically be nothrow?
This could have the special effect that in cases where MyClass is used as the declared type, no exception handling would have to be done, while that would still be the case for MyInterface. If that's possible it would be another small improvement over Java.
--
Marco
Continue reading on narkive:
Loading...