September 29, 2014
On 9/29/14 7:53 AM, Walter Bright wrote:
> On 9/29/2014 4:47 AM, Steven Schveighoffer wrote:
>> On 9/27/14 9:52 PM, Walter Bright wrote:
>>> 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.
>>
>> OK, so if you want to avoid improperly having errors/exceptions, don't
>> put bugs
>> into your code.
>>
>> A simple plan!
>
> Validating user input is not the same thing as removing all the logic
> bugs from the program.
>

What if it's not user input?

-Steve
September 29, 2014
On 9/29/14 7:54 AM, Walter Bright wrote:
> On 9/29/2014 4:51 AM, Steven Schveighoffer wrote:
>> What about:
>>
>> File f;
>> f.open(null);
>>
>> Is that an environmental error or User error?
>
> Passing invalid arguments to a function is a programming bug.
>

That throws an exception. My point exactly.

-Steve
September 29, 2014
On 29/09/14 13:51, Steven Schveighoffer wrote:

> That makes no sense. The opening of the file is subject to issues with
> the filesystem, which means they may be environmental or user errors,
> not programming errors. But that doesn't mean the opening of the file
> failed because the file doesn't exist, it could be an error in how you
> construct the file name.
>
> What about:
>
> File f;
> f.open(null);
>
> Is that an environmental error or User error?

That depends on what "open" expect from its argument. In this particular case, in D, "null" is the same as the empty string. I don't see why that technically shouldn't be allowed.

Of course, you can specify that "open" expects a string argument with the length that is greater then 0, in that case it's a bug by the programmer that uses the "open" function.

-- 
/Jacob Carlborg
September 29, 2014
On 9/29/14 10:48 AM, Jacob Carlborg wrote:
> On 29/09/14 13:51, Steven Schveighoffer wrote:
>
>> That makes no sense. The opening of the file is subject to issues with
>> the filesystem, which means they may be environmental or user errors,
>> not programming errors. But that doesn't mean the opening of the file
>> failed because the file doesn't exist, it could be an error in how you
>> construct the file name.
>>
>> What about:
>>
>> File f;
>> f.open(null);
>>
>> Is that an environmental error or User error?
>
> That depends on what "open" expect from its argument. In this particular
> case, in D, "null" is the same as the empty string. I don't see why that
> technically shouldn't be allowed.

My entire point is, it doesn't matter what is expected or what is treated as "correct." what matters is where the input CAME from. To a library function, it has no idea. There is no extra type info saying "this parameter comes from user input."

> Of course, you can specify that "open" expects a string argument with
> the length that is greater then 0, in that case it's a bug by the
> programmer that uses the "open" function.

Is it? I can think of cases where it's programmer error, and cases where it's user error.

There are also better example functions, but I'm focusing on File because that's what this thread is about.

The largest issue I see with this whole scheme is that exceptions can be turned into errors, but not the reverse. Once an error is thrown, it's pretty much game over. So defensive coding would suggest when you don't know the answer, throw an exception, and something higher up would say "Oh, that is really a program error, rethrowing"

But expecting developers to do this at EVERY CALL is really impossible.

My expectation is that an exception is really an error unless caught. It would be nice to postpone generating the stack trace unless the exception is caught outside the main function. I don't know enough about how exceptions work to know if this is possible or not.

-Steve
September 29, 2014
On Monday, 29 September 2014 at 00:47:58 UTC, Walter Bright wrote:
> On 9/28/2014 4:18 PM, Joseph Rushton Wakeling via Digitalmars-d wrote:
>> I don't follow this point.  How can this approach work with programs that are
>> built with the -release switch?
>
> All -release does is not generate code for assert()s. To leave the asserts in, do not use -release. If you still want the asserts to be in even with -release,
>
>     if (condition) assert(0);

The reason I queried your approach here is because I feel you're conflating two things:

    * the _definition_ of an Exception vs. an Error, on which we 100%
      agree: the former as an anticipated possibility which a program
      is committed to try and handle, the latter a failure which is
      fundamentally wrong and should not happen under any conditions.

    * the way in which a program should report these different kinds
      of error.

You seem to be advocating that, by definition, Exceptions and Errors should be reported differently (one without, and one with, a trace).  I don't at all object to that as a sensible default, but I think that the ultimate decision on how Exceptions and Errors report themselves should be in the hands of the program developer, depending on the use-case of the application.
September 29, 2014
On Monday, 29 September 2014 at 15:39:08 UTC, Joseph Rushton Wakeling wrote:
> with, a trace).  I don't at all object to that as a sensible default, but I think that the ultimate decision on how Exceptions and Errors report themselves should be in the hands of the program developer, depending on the use-case of the application.

That's a very sensible argument. From a pragmatic point of view I am usually not interested in the whole stack trace.

I am primarily interested in where it was thrown, where it was turned into an error and where it was logged + "function call input" at that point.

Maybe a trade off can be found that is less costly than building a full stack trace.
September 29, 2014
On Mon, Sep 29, 2014 at 8:13 AM, Steven Schveighoffer via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> My entire point is, it doesn't matter what is expected or what is treated as "correct." what matters is where the input CAME from. To a library function, it has no idea. There is no extra type info saying "this parameter comes from user input."


>From the method's view, parameters passed in are user input.  Full stop.

One thing that seems to be talked around a bit here is the separation/encapsulation of things.  It is perfectly fine, and expected, for a library method to throw exceptions on bad input to the method - even if this input turns out to be a programming bug elsewhere.  From the standpoint of the method, it does not know (and does not care) where the thing ultimately came from - all it knows is that it is input here, and it is wrong.

If you call a method with bad input, and fail to catch the resulting exception, then _that_ is a bug, not the method throwing.  It may be perfectly recoverable to ignore/retry/whatever, or it may be a symptom of something that should abort the program.  But the method throwing does not know or care.


September 29, 2014
On 9/29/14 2:43 PM, Jeremy Powers via Digitalmars-d wrote:
> On Mon, Sep 29, 2014 at 8:13 AM, Steven Schveighoffer via Digitalmars-d
> <digitalmars-d@puremagic.com <mailto:digitalmars-d@puremagic.com>> wrote:
>
>     My entire point is, it doesn't matter what is expected or what is
>     treated as "correct." what matters is where the input CAME from. To
>     a library function, it has no idea. There is no extra type info
>     saying "this parameter comes from user input."
>
>
>  From the method's view, parameters passed in are user input.  Full stop.

This is missing the point of an exception. An uncaught exception is an error which crashes the program. If you catch the exception, you can handle it, but if you don't expect it, then it's a bug. Any uncaught exceptions are BY DEFINITION programming errors.

> One thing that seems to be talked around a bit here is the
> separation/encapsulation of things.  It is perfectly fine, and expected,
> for a library method to throw exceptions on bad input to the method -
> even if this input turns out to be a programming bug elsewhere.  From
> the standpoint of the method, it does not know (and does not care) where
> the thing ultimately came from - all it knows is that it is input here,
> and it is wrong.

What is being discussed here is removing the stack trace and printout when an exception is thrown.

Imagine this error message:

myprompt> ./mycoolprogram
(1 hour later)
FileException: Error opening file xgghfsnbuer
myprompt>

Now what? xgghfsnbuer may not even be in the code anywhere. There may be no hints at all as to what caused it to happen. You don't even know which line of code YOU wrote that was causing the issue! You have to examine every File open, and every call that may have opened a file, and see if possibly that file name was passed into it.

Whereas if you get a trace, you can at least see where the exception occurred, and start from there.

Now, RELYING on this printout to be your interface to the user, that is incorrect design, I will agree. But one cannot possibly be expected to handle every possible exception at every possible call so one can throw an error in the cases where it actually is an error. D doesn't even require listing the exceptions that may be thrown on the API (and no, I'm not suggesting it should).

> If you call a method with bad input, and fail to catch the resulting
> exception, then _that_ is a bug, not the method throwing.  It may be
> perfectly recoverable to ignore/retry/whatever, or it may be a symptom
> of something that should abort the program.  But the method throwing
> does not know or care.
>

Sure, but it doesn't happen. Just like people do not check return values from syscalls.

The benefit of the exception printing is at least you get a trace of where things went wrong when you didn't expect them to. Ignoring a call's return value doesn't give any notion something is wrong until much later.

-Steve
September 29, 2014
On Mon, Sep 29, 2014 at 8:13 AM, Steven Schveighoffer via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> The largest issue I see with this whole scheme is that exceptions can be turned into errors, but not the reverse. Once an error is thrown, it's pretty much game over. So defensive coding would suggest when you don't know the answer, throw an exception, and something higher up would say "Oh, that is really a program error, rethrowing"
>
> But expecting developers to do this at EVERY CALL is really impossible.
>

And this is an argument for checked exceptions - being able to explicitly
state 'these are known fatal cases for this component, you should deal with
them appropriately' when defining a method.  Cuts down the catch/check to
just the common cases, and makes such cases explicit to the caller.
Anything not a checked exception falls into the 'error, abort!' path.
 (Memory corruption etc. being abort scenarios)

If I really needed to write a robust program in D right now, I would (attempt) to wrap every call in a try/catch, and check if the thrown exception was of a handleable type.  But knowing which types for which methods would lead me to basically hacking up some documentation-enforced checked exceptions, and being entirely unmaintainable.


September 29, 2014
On Monday, 29 September 2014 at 05:15:14 UTC, Walter Bright wrote:
>
> I confess much skepticism about such things when it comes to software. I do know how reliable avionics software is done, and that stuff does work even in the face of all kinds of bugs, damage, and errors. I'll be betting my life on that tomorrow :-)
>
> Would you bet your life on software that had random divide by 0 bugs in it that were just ignored in the hope that they weren't serious? Keep in mind that software is rather unique in that a single bit error in a billion bytes can render the software utterly demented.

I'm not saying the errors should be ignored, but rather that
there are other approaches to handling errors besides (or in
addition to) terminating the process.  For me, the single most
important thing is detecting errors as soon as possible so
corrective action can be taken before things go too far south (so
hooray for contracts!).  From there, the proper response depends
on the error detected and the type of system I'm working on.
Like with persistent stateful systems, even if a restart occurs
can you assume that the persisted state is valid?  With a mesh of
communicating systems, if one node goes insane, what impact might
it have on other nodes in the network?  I think the definition of
what constitutes an interdependent system is application defined.

And yes, I know all about tiny bugs creating insane problems.
With event-based asynchronous programming, the most common
serious bugs I encounter memory corruption problems from dangling
pointers, and the only way to find and fix these is by analyzing
gigabytes worth of log files to try and unpack what happened
after the fact.  Spending a day looking at the collateral damage
from what ultimately turns out to be a backwards conditional
expression in an error handler somewhere gives a pretty healthy
respect for the brittleness of memory unsafe code.  This is one
area where having a GC is an enormous win.

> Remember the Apollo 11 lunar landing, when the descent computer software started showing self-detected faults? Armstrong turned it off and landed manually. He wasn't going to bet his ass that the faults could be ignored. You and I wouldn't, either.

And this is great if there's a human available to take over.  But
what if this were a space probe?


>> I think what I'm trying to say is that simply aborting on error is too brittle in some cases, because it only deals with one vector--memory corruption that is unlikely to reoccur.  But I've watched always-on systems fall apart from some unexpected ongoing situation, and simply restarting doesn't actually help.
>
> In such a situation, ignoring the error seems hardly likely to do any better.

Again, not ignoring, but rather that a restart may not be the
appropriate response to the problem.  Or it may be a part of the
appropriate response, but other things need to happen as well.