April 03, 2013
On Wednesday, 3 April 2013 at 09:20:02 UTC, Walter Bright wrote:
> On 4/2/2013 1:00 PM, Lars T. Kyllingstad wrote:
>> I definitely don't think we need an IllegalArgumentException. IMO, passing
>> illegal arguments is 1) a simple programming error, in which case it should be
>> an Error, or 2) something the programmer can not avoid, in which case it
>> requires a better description of why things went wrong than just "illegal
>> argument".  "File not found", for example.
>>
>> I didn't really consider contracts when I wrote the DIP, and of course there
>> will always be the problem of "should this be a contract or a normal input
>> check?"  The problem with contracts, though, is that they go away in release
>> mode, which is certainly not safe if that is your error handling mechanism.
>
> A bit of philosophy here:
>
> Contracts are not there to validate user input. They are only there to check for logic bugs in the program itself.
>
> It's a very clear distinction, and should not be a problem.
>
> To reiterate, if a contract fails, that is a BUG in the program, and the program is then considered to be in an undefined state and is not recoverable.
>
> CONTRACTS ARE NOT AN ERROR HANDLING MECHANISM.

I completely agree, and this is exactly why we need InputArgumentError.

Lars
April 03, 2013
On Wednesday, 3 April 2013 at 08:37:32 UTC, deadalnix wrote:
> On Tuesday, 2 April 2013 at 20:11:31 UTC, Lars T. Kyllingstad wrote:
>> No.  To call front on an empty range is a programming error, plain and simple.  It's like trying to access the first element of an empty array.  The fact that some ranges may allow it anyway does not change anything.
>>
>
> It is illegal for a reason. For instance, with an array, it is an out of bound access. I see ne benefice to hide this information in a more generic RangeError. This is hiding information and providing nothing more.

For arrays, RangeError is synonymous with "out of bounds".  I see no reason to invent a new class just for this purpose.

And note that I'm not saying that ranges should be restricted to *only* throwing RangeErrors.  Generally, it should be used in situations that are analogous to out of bounds for arrays, such as trying to pop past the end of the range.

However, some ranges may want to do something else in this situation.  An output range that writes to a file, for instance, may want to throw a "disk full" exception.  A wrapper range may simply propagate any errors from the underlying range.

RangeError is for the cases when it is not possible/necessary to provide more detail than "you tried to call popFront on an empty range, which is illegal".

Lars
April 03, 2013
On Wednesday, 3 April 2013 at 08:40:52 UTC, deadalnix wrote:
> On Tuesday, 2 April 2013 at 20:11:31 UTC, Lars T. Kyllingstad wrote:
>> Phobos/druntime devs can always add to the enums.  Users still have the option of subclassing if strictly necessary.
>>
>
> This is fundamentally incompatible with :
>
>> I'm not sure I agree with that rule.  And anyway, D's final switch mitigate some of the problems with classic switch.
>>
>
> As adding an entry unto the enum will break every single final switch in user code.

I don't see the incompatibility.  This is exactly the purpose of final switch.  If the user didn't want to be forced to handle a new error category, they'd use normal switch instead.

You have yet to specify the problems with switch in OOP.  Maybe you meant something else, that final switch doesn't solve?

Lars
April 03, 2013
On Wed, 03 Apr 2013 03:12:56 -0400, Lars T. Kyllingstad <public@kyllingen.net> wrote:

> On Wednesday, 3 April 2013 at 07:01:09 UTC, Jacob Carlborg wrote:
>> On 2013-04-02 22:15, Lars T. Kyllingstad wrote:
>>
>>> This illustrates my point nicely!  What does the shell do in this case?
>>> It treats both errors the same:  It prints an error message and returns
>>> to the command line.  It does not magically try to guess the filename,
>>> find a way to get you permission, etc.
>>
>> No, but you do know the difference. It doesn't just say "can't open file <filename>". It will say either, "file <filename> doesn't exist" or "don't have permission to access <filename>". It's a huge difference. I know _what_ went wrong with that file, not just that _something_ when wrong.
>
> Which is exactly what you'd use FilesystemException.kind and/or FilesystemException.msg for.
>
> I never said there shouldn't be a way to distinguish between file errors, I just said that in most cases, an entirely new exception class is overkill.

Think of it this way, is there a use case where you would want to catch the "file not found" exception, but let the "do not have permission" exception go through to the next handler?  or vice versa?  This is the only reason to have separate classes.

Then we are in the awkward position of what to do if we need to catch both.  In that case, you have to have a base class that covers both.

The problem I have with this whole scheme of one class per error type is, you inevitably cannot cover everyone's use case, so they end up having to catch a base class and then doing the work to figure out what it is manually.

-Steve
April 03, 2013
On Wednesday, 3 April 2013 at 14:11:09 UTC, Steven Schveighoffer wrote:

> The problem I have with this whole scheme of one class per error type is, you inevitably cannot cover everyone's use case, so they end up having to catch a base class and then doing the work to figure out what it is manually.

Precisely.  And then, a switch over an enum is both way more efficient and more readable than a bunch of ifs and casts.

Lars
April 03, 2013
On Wed, 03 Apr 2013 01:59:32 -0400, Lars T. Kyllingstad <public@kyllingen.net> wrote:

> On Wednesday, 3 April 2013 at 05:42:13 UTC, Lars T. Kyllingstad wrote:
>> The problem with assert is that it gets disabled in release mode.
>>  I think it is a bad idea to have this be the "standard" behaviour of parameter validation.
>
> Allow me to clarify my position on assert() a bit.  As a general rule, I think assert() should be used to verify internal program flow and program invariants, and nothing more.  Specifically, public APIs should *not* change depending on whether asserts are compiled in or not.
>
> Say I am writing a function that you are using.  I don't trust you to always give me correct parameters, so I check them.  (Maybe my function could even do something dangerous if I didn't.)
>
>    public void myFunction(someArgs)
>    {
>        if (someArgs are invalid)
>            throw new InvalidArgumentError;
>        ...
>    }

I disagree here.  There are two "users" involved, one is the actual user, typing a command on the command line, and then the developer who uses the function.  The developer should be checked with assert, the user should be checked with code like you wrote.

The problem becomes apparent when developers don't check user input before passing to your functions.  That is on them, not you.  The library should be able to have all the safety checks removed to improve performance.

I wish there was a way to say "this data is unchecked" or "this data is checked and certified to be correct" when you call a function.  That way you could run the in contracts on user-specified data, even with asserts turned off, and avoid the checks in release code when the data has already proven valid.

-Steve
April 03, 2013
On Wednesday, 3 April 2013 at 14:32:59 UTC, Steven Schveighoffer wrote:
> I wish there was a way to say "this data is unchecked" or "this data is checked and certified to be correct" when you call a function.  That way you could run the in contracts on user-specified data, even with asserts turned off, and avoid the checks in release code when the data has already proven valid.
>
> -Steve

But you can. It is not much different from Range vs SortedRange, type systems exists exactly to solve such issues. Using UDA is probably even more elegant, but that is implementation details.

And it is one of big improvements both on safety and performance side you can get from using strongly typed languages in network services.
April 03, 2013
On Wednesday, 3 April 2013 at 09:40:27 UTC, Lars T. Kyllingstad wrote:
> For arrays, RangeError is synonymous with "out of bounds".  I see no reason to invent a new class just for this purpose.
>

Exactly, no need for RangeError (as out of bound access can be produced in many situation that don't involve ranges).

> And note that I'm not saying that ranges should be restricted to *only* throwing RangeErrors.  Generally, it should be used in situations that are analogous to out of bounds for arrays, such as trying to pop past the end of the range.
>

The data always come from somewhere. That somewhere can't provide anymore data for a reason. Because you reached the end of a file (IOException or something) because the network disconnected (NetworkException) or whatever.

RangeError imply that the data magically appears from a range, without any actual source, which is impossible.

> However, some ranges may want to do something else in this situation.  An output range that writes to a file, for instance, may want to throw a "disk full" exception.  A wrapper range may simply propagate any errors from the underlying range.
>

Exactly. In general, wrapper should simply forward calls and let the source fail when its own logic isn't involved.

> RangeError is for the cases when it is not possible/necessary to provide more detail than "you tried to call popFront on an empty range, which is illegal".
>

It is always illegal for a reason.
April 03, 2013
On Wednesday, 3 April 2013 at 14:32:59 UTC, Steven Schveighoffer wrote:
> On Wed, 03 Apr 2013 01:59:32 -0400, Lars T. Kyllingstad <public@kyllingen.net> wrote:
>>
>> Say I am writing a function that you are using.  I don't trust you to always give me correct parameters, so I check them.  (Maybe my function could even do something dangerous if I didn't.)
>>
>>   public void myFunction(someArgs)
>>   {
>>       if (someArgs are invalid)
>>           throw new InvalidArgumentError;
>>       ...
>>   }
>
> I disagree here.  There are two "users" involved, one is the actual user, typing a command on the command line, and then the developer who uses the function.  The developer should be checked with assert, the user should be checked with code like you wrote.
>
> The problem becomes apparent when developers don't check user input before passing to your functions.  That is on them, not you.  The library should be able to have all the safety checks removed to improve performance.

Some, yes, but not all.  You always have to weigh the benefit, i.e. the improved performance, against the drawbacks, i.e. reduced safety.  If you are removing trivial safety checks from a function that performs a very expensive and possibly dangerous operation -- a disk operation, say -- you're doing something wrong.

I agree it should be possible to remove safety checks from functions which are expected to be performant, and where the checks will have an impact (e.g. the range primitives), but it should be done with a less attractive compiler switch than -release.

I think it's a big mistake to encourage programmers to ship their programs with array bounds checks and the like disabled.  Such programs should be the exception, not the rule.  It's always better to err on the side of safety rather than performance.


> I wish there was a way to say "this data is unchecked" or "this data is checked and certified to be correct" when you call a function.  That way you could run the in contracts on user-specified data, even with asserts turned off, and avoid the checks in release code when the data has already proven valid.

That would be awesome indeed.

Lars
April 03, 2013
On Wednesday, 3 April 2013 at 09:44:23 UTC, Lars T. Kyllingstad wrote:
> I don't see the incompatibility.  This is exactly the purpose of final switch.  If the user didn't want to be forced to handle a new error category, they'd use normal switch instead.
>

This is a good thing in your own code. But in phobos, this is a guarantee to break a lot of user code when adding to the enum.

> You have yet to specify the problems with switch in OOP.  Maybe you meant something else, that final switch doesn't solve?
>

The idiomatic way to execute code depending on a value in OOP is virtual dispatch.