Discussion:
Program logic bugs vs input/environmental errors
(too old to reply)
Walter Bright via Digitalmars-d
2014-09-27 23:15:40 UTC
Permalink
This issue comes up over and over, in various guises. I feel like Yosemite Sam here:



In that vein, Exceptions are for either being able to recover from
input/environmental errors, or report them to the user of the application.

When I say "They are NOT for debugging programs", I mean they are NOT for
debugging programs.

assert()s and contracts are for debugging programs.
dmd test.d
test.d(15) Error: missing } thrown from dmd/src/parse.c(283)

?

See:

https://issues.dlang.org/show_bug.cgi?id=13543

As for the programmer wanting to know where the message "missing }" came from,

grep -r dmd/src/*.c "missing }"

works nicely. I do that sort of thing all the time. It really isn't a problem.
bearophile via Digitalmars-d
2014-09-27 23:33:50 UTC
Permalink
Post by Walter Bright via Digitalmars-d
As for the programmer wanting to know where the message
"missing }" came from,
grep -r dmd/src/*.c "missing }"
works nicely. I do that sort of thing all the time. It really
isn't a problem.
grep is not useful for the purposes explained in issue 13543
because the file name is often inside a string variable, that is
initialized elsewhere or generated in some way. So the exception
is useful to know where's the instruction in user code that has
tried the failed I/O action, as I've explained in that issue.

Bye,
bearophile
Walter Bright via Digitalmars-d
2014-09-27 23:42:18 UTC
Permalink
Post by Walter Bright via Digitalmars-d
As for the programmer wanting to know where the message "missing }" came from,
grep -r dmd/src/*.c "missing }"
works nicely. I do that sort of thing all the time. It really isn't a problem.
grep is not useful for the purposes explained in issue 13543 because the file
name is often inside a string variable, that is initialized elsewhere or
generated in some way. So the exception is useful to know where's the
instruction in user code that has tried the failed I/O action, as I've explained
in that issue.
Even if that is what you wanted, you won't get that from FileException, as it
will only show file/lines emanating from calls inside std.file, not from higher
level callers.

Besides, take a bit of care when formulating a string for exceptions, and you
won't have any trouble grepping for it. This isn't rocket science.

Presenting internal debugging data to users for input/environmental errors is
just bad programming practice. We shouldn't be enshrining it in Phobos and
presenting it as a professional way to code.
H. S. Teoh via Digitalmars-d
2014-09-27 23:55:53 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by Walter Bright via Digitalmars-d
As for the programmer wanting to know where the message "missing }" came from,
grep -r dmd/src/*.c "missing }"
works nicely. I do that sort of thing all the time. It really isn't a problem.
grep is not useful for the purposes explained in issue 13543 because
the file name is often inside a string variable, that is initialized
elsewhere or generated in some way. So the exception is useful to
know where's the instruction in user code that has tried the failed
I/O action, as I've explained in that issue.
Even if that is what you wanted, you won't get that from
FileException, as it will only show file/lines emanating from calls
inside std.file, not from higher level callers.
Besides, take a bit of care when formulating a string for exceptions,
and you won't have any trouble grepping for it. This isn't rocket
science.
Presenting internal debugging data to users for input/environmental
errors is just bad programming practice. We shouldn't be enshrining it
in Phobos and presenting it as a professional way to code.
My take on this, is that uncaught exceptions are a program bug. Any
messages displayed to the user ought to come from a catch block that not
only prints the exception message (*without* things like line numbers
and stack traces, btw), but also provides context (e.g., "Error in
configuration file section 'abc': illegal field value" instead of just
"illegal field value" with no context of where it might have been
triggered).

Uncaught exceptions (which ideally should only be Errors, not
Exceptions) are a program bug that ought to be fixed. In the case that
somehow one managed to elude your catch blocks, the full debug infodump
(source file, line number, stack trace) is useful for users to hand back
to you in a bug report, so that you can track down the problem. The user
should not be expected to understand the infodump from an uncaught
exception, whereas a message printed from a catch block ought to be
user-understandable (like "can't open 'myphoto.jpg': file not found",
not "internal error on line 12345" which makes no sense to a user).


T
--
Laissez-faire is a French term commonly interpreted by Conservatives to mean 'lazy fairy,' which is the belief that if governments are lazy enough, the Good Fairy will come down from heaven and do all their work for them.
bearophile via Digitalmars-d
2014-09-27 23:59:43 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Even if that is what you wanted, you won't get that from
FileException, as it will only show file/lines emanating from
calls inside std.file, not from higher level callers.
Can't we use the template arguments like __LINE__ to offer the
line of code in the IO function in user code?
Post by Walter Bright via Digitalmars-d
Besides, take a bit of care when formulating a string for
exceptions, and you won't have any trouble grepping for it.
This isn't rocket science.
The exception is generated inside library code, not in user code.
There's nothing to grep in the user code. The D script often
looks like this:

void main() {
import std.stdio;
auto file_name = "some_file;
// some code here
auto file_name1 = file_name ~ "1.txt";
auto f1 = File(file_name1);
// some code here
auto file_name2 = file_name ~ "2.txt";
auto f2 = File(file_name2, "w");
// some code here
}
Post by Walter Bright via Digitalmars-d
Presenting internal debugging data to users for
input/environmental errors is just bad programming practice.7
We shouldn't be enshrining it in Phobos and presenting it as a
professional way to code.
The file/line of code is not meant to replace serious practices
for dealing with I/O failures in serious D programs.

Bye,
bearophile
Walter Bright via Digitalmars-d
2014-09-28 00:54:09 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Even if that is what you wanted, you won't get that from FileException, as it
will only show file/lines emanating from calls inside std.file, not from
higher level callers.
Can't we use the template arguments like __LINE__ to offer the line of code in
the IO function in user code?
We could, but again, WHOA CAMEL!

Also, one should be careful in using __LINE__ in a template, as it will be
forced to generate a new instantiation with every use.
Post by Walter Bright via Digitalmars-d
Besides, take a bit of care when formulating a string for exceptions, and you
won't have any trouble grepping for it. This isn't rocket science.
The exception is generated inside library code, not in user code. There's
void main() {
import std.stdio;
auto file_name = "some_file;
// some code here
auto file_name1 = file_name ~ "1.txt";
auto f1 = File(file_name1);
// some code here
auto file_name2 = file_name ~ "2.txt";
auto f2 = File(file_name2, "w");
// some code here
}
Come on. What is hard about "Cannot open file `xxxx` in mode `xxxx`", then you
grep for "Cannot open file" ?
grep "Cannot open file" *.d
stdio.d: text("Cannot open file `", name, "' in mode `",
stdio.d:// new StdioException("Cannot open file `"~fName~"' for reading"));
stdio.d:// new StdioException("Cannot open file `"~fName~"' for reading"));

Besides, File doesn't throw a FileException and doesn't add caller's __FILE__
and __LINE__.

If you're seriously proposing adding __FILE__ and __LINE__ to every function
call, then D is the wrong language. This would have disastrous bloat and
performance problems. You mentioned Python and Ruby, both of which are well
known for miserable performance.

Building the equivalent of a symbolic debugger into RELEASED D code is just not
acceptable.
Post by Walter Bright via Digitalmars-d
Presenting internal debugging data to users for input/environmental errors is
just bad programming practice.7 We shouldn't be enshrining it in Phobos and
presenting it as a professional way to code.
The file/line of code is not meant to replace serious practices for dealing with
I/O failures in serious D programs.
That's exactly what you're doing with it.
Walter Bright via Digitalmars-d
2014-09-28 00:40:22 UTC
Permalink
Post by H. S. Teoh via Digitalmars-d
My take on this, is that uncaught exceptions are a program bug.
Not to me. Custom messages would be better, but the exception message should be
serviceable.
Post by H. S. Teoh via Digitalmars-d
Uncaught exceptions (which ideally should only be Errors, not
Exceptions) are a program bug that ought to be fixed. In the case that
somehow one managed to elude your catch blocks, the full debug infodump
(source file, line number, stack trace) is useful for users to hand back
to you in a bug report, so that you can track down the problem. The user
should not be expected to understand the infodump from an uncaught
exception, whereas a message printed from a catch block ought to be
user-understandable (like "can't open 'myphoto.jpg': file not found",
not "internal error on line 12345" which makes no sense to a user).
Whoa, Camel! You're again thinking of Exceptions as a debugging tool.
Timon Gehr via Digitalmars-d
2014-09-28 00:54:34 UTC
Permalink
Post by H. S. Teoh via Digitalmars-d
My take on this, is that uncaught exceptions are a program bug.
Not to me. ...
It is not worth fixing if a program terminates with a stack trace?
Walter Bright via Digitalmars-d
2014-09-28 01:16:50 UTC
Permalink
Post by Timon Gehr via Digitalmars-d
Post by H. S. Teoh via Digitalmars-d
My take on this, is that uncaught exceptions are a program bug.
Not to me. ...
It is not worth fixing if a program terminates with a stack trace?
I never was in favor of adding the stack trace output, either, for the same
reason. Exceptions are not programming bugs.

For Errors, a case can be made for them.
Sean Kelly via Digitalmars-d
2014-09-28 16:16:07 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Whoa, Camel! You're again thinking of Exceptions as a debugging tool.
They can be. What if an API you're using throws an exception you
didn't expect, and therefore don't handle? This might be
considered a logic error if the exception is recoverable and you
don't intend the program to abort from that operation.
Sean Kelly via Digitalmars-d
2014-09-28 16:23:40 UTC
Permalink
On Sunday, 28 September 2014 at 00:40:26 UTC, Walter Bright
Post by Walter Bright via Digitalmars-d
Whoa, Camel! You're again thinking of Exceptions as a
debugging tool.
They can be. What if an API you're using throws an exception
you didn't expect, and therefore don't handle? This might be
considered a logic error if the exception is recoverable and
you don't intend the program to abort from that operation.
Also, I think the idea that a program is created and shipped to
an end user is overly simplistic. In the server/cloud
programming world, when an error occurs, the client who submitted
the request will get a response appropriate for them and the
system will also generate log information intended for people
working on the system. So things like stack traces and assertion
failure information is useful even for production software. Same
with any critical system, as I'm sure you're aware. The systems
are designed to handle failures in specific ways, but they also
have to leave a breadcrumb trail so the underlying problem can be
diagnosed and fixed. Internal testing is never perfect, and
achieving a high coverage percentage is nearly impossible if the
system wasn't designed from the ground up to be testable in such
a way (mock frameworks and such).
Walter Bright via Digitalmars-d
2014-09-28 17:32:14 UTC
Permalink
Post by Sean Kelly via Digitalmars-d
Post by Walter Bright via Digitalmars-d
Whoa, Camel! You're again thinking of Exceptions as a debugging tool.
They can be.
Of course they can be. But it's inappropriate to use them that way, and we
should not be eschewing such in the library.
Post by Sean Kelly via Digitalmars-d
What if an API you're using throws an exception you didn't expect,
and therefore don't handle?
Then the app user sees the error message. This is one of the cool things about D
- I can write small apps with NO error handling logic in it, and I still get
appropriate and friendly messages when things go wrong like missing files.

That is, until recently, when I get a bunch of debug stack traces and internal
file/line messages, which are of no use at all to an app user and look awful.
Post by Sean Kelly via Digitalmars-d
This might be considered a logic error if the
exception is recoverable and you don't intend the program to abort from that
operation.
Adding file/line to all exceptions implies that they are all bugs, and
encourages them to be thought of as bugs and debugging tools, when they are NOT.
Exceptions are for:

1. enabling recovery from input/environmental errors
2. reporting input/environmental errors to the app user
3. making input/environmental errors not ignorable by default

They are not for detecting logic errors. Assert is designed for that.
Steven Schveighoffer via Digitalmars-d
2014-09-28 01:24:00 UTC
Permalink
Post by Walter Bright via Digitalmars-d
When I say "They are NOT for debugging programs", I mean they are NOT
for debugging programs.
Library code often cannot make that choice. The issue with exceptions
vs. errors is that often you don't know where the input comes from.

e.g.:

auto f = File(someInternalStringThatIsCorrupted) -> error
auto f = File(argv[1]) -> exception

How does File know what it's target file name came from?

-Steve
Walter Bright via Digitalmars-d
2014-09-28 01:52:11 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d
Post by Walter Bright via Digitalmars-d
When I say "They are NOT for debugging programs", I mean they are NOT
for debugging programs.
Library code often cannot make that choice.
The issue with exceptions vs. errors
is that often you don't know where the input comes from.
auto f = File(someInternalStringThatIsCorrupted) -> error
auto f = File(argv[1]) -> exception
How does File know what it's target file name came from?
If the app is concerned about invalid filenames as bugs, you should scrub the
filenames first. I.e. the interface is defined improperly if the code confuses a
programming bug with input errors.
Jacob Carlborg via Digitalmars-d
2014-09-28 09:01:13 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d
Library code often cannot make that choice. The issue with exceptions
vs. errors is that often you don't know where the input comes from.
auto f = File(someInternalStringThatIsCorrupted) -> error
auto f = File(argv[1]) -> exception
How does File know what it's target file name came from?
Both of theses should throw an exception. Most stuff related to file
operations should throw an exception, not an error.
--
/Jacob Carlborg
ketmar via Digitalmars-d
2014-09-28 01:30:58 UTC
Permalink
On Sat, 27 Sep 2014 16:15:40 -0700
After all, what would you think of a compiler that spewed out
dmd test.d
test.d(15) Error: missing } thrown from dmd/src/parse.c(283)
?
"wow, that's cool! one more pleasing feature!"
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: not available
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20140928/e12d80ff/attachment.sig>
luka8088 via Digitalmars-d
2014-09-28 09:20:25 UTC
Permalink
Post by Walter Bright via Digitalmars-d
http://youtu.be/hBhlQgvHmQ0
In that vein, Exceptions are for either being able to recover from
input/environmental errors, or report them to the user of the application.
When I say "They are NOT for debugging programs", I mean they are NOT
for debugging programs.
assert()s and contracts are for debugging programs.
dmd test.d
test.d(15) Error: missing } thrown from dmd/src/parse.c(283)
?
https://issues.dlang.org/show_bug.cgi?id=13543
As for the programmer wanting to know where the message "missing }" came from,
li
grep -r dmd/src/*.c "missing }"
works nicely. I do that sort of thing all the time. It really isn't a problem.
We has this issue at work (we are working with php). We outputted a
stack trace for both exceptions and asserts but the lines that should be
addressed are not always so obvious.

I found a solution and it works great for us. All library code is marked
appropriately so when stack is outputted it is shadows out (with gray
color) all the lines in library code and point out first non-library
line from the top of the stack. In 95% of the time it is the line that
the programmer should look into. Other 5% is the time when it shows the
line where programmer is forwarding a call to the library but turns out
to be ok as it turns out to be much more comprehensible than the entire
stack. One note worth mentioning is that juniors have much easier time
understanding which lines concern them, and from that I can only
conclude that such approach is more intuitive.

Marking is done on namespace level so it can be easily disabled for
entire namespace.

I think outputting a stack trace for asserts is a must because of that
5%. And for exceptions I agree completely with your arguments and I
think that there is no need for stack.

From my experience this has been a good approach and I think is worth
considering.
bearophile via Digitalmars-d
2014-09-28 09:28:13 UTC
Permalink
All library code is marked appropriately so when stack is
outputted it is shadows out (with gray color) all the lines in
library code and point out first non-library line from the top
of the stack. In 95% of the time it is the line that the
programmer should look into. Other 5% is the time when it shows
the line where programmer is forwarding a call to the library
but turns out to be ok as it turns out to be much more
comprehensible than the entire stack. One note worth mentioning
is that juniors have much easier time understanding which lines
concern them, and from that I can only conclude that such
approach is more intuitive.
This looks like a little enhancement request, to colourize
differently the parts of the stack trace of the library
code/runtime from the user code.
And for exceptions I agree completely with your arguments and I
think that there is no need for stack.
I think Walter is not suggesting to remove the stack trace for
exceptions.

Bye,
bearophile
Ary Borenszweig via Digitalmars-d
2014-09-28 15:10:24 UTC
Permalink
Post by Walter Bright via Digitalmars-d
http://youtu.be/hBhlQgvHmQ0
In that vein, Exceptions are for either being able to recover from
input/environmental errors, or report them to the user of the application.
When I say "They are NOT for debugging programs", I mean they are NOT
for debugging programs.
assert()s and contracts are for debugging programs.
For me, assert is useless.

We are developing a language using LLVM as our backend. If you give LLVM
something it doesn't like, you get something this:

~~~
Assertion failed: (S1->getType() == S2->getType() && "Cannot create
binary operator with two operands of differing type!"), function Create,
file Instructions.cpp, line 1850.

Abort trap: 6
~~~

That is what the user gets when there is a bug in the compiler, at least
when we are generating invalid LLVM code. And that's one of the good
paths, if you compiled LLVM with assertions, because otherwise I guess
it's undefined behaviour.

What I'd like to do, as a compiler, is to catch those errors and tell
the user: "You've found a bug in the app, could you please report it in
this URL? Thank you.". We can't: the assert is there and we can't change it.

Now, this is when you interface with C++/C code. But inside our language
code we always use exceptions so that programmers can choose what to do
in case of an error. With assert you loose that possibility.

Raising an exception is costly, but that should happen in exceptional
cases. Installing an exception handler is cost-free, so I don't see why
there is a need for a less powerful construct like assert.
Xiao Xie via Digitalmars-d
2014-09-28 16:25:41 UTC
Permalink
On Sunday, 28 September 2014 at 15:10:26 UTC, Ary Borenszweig
Post by Ary Borenszweig via Digitalmars-d
What I'd like to do, as a compiler, is to catch those errors
and tell the user: "You've found a bug in the app, could you
please report it in this URL? Thank you.". We can't: the assert
is there and we can't change it.
Why is SIGABRT handler not working for your usecase? print and
exit?
Loading...