Athari / YaLinqo

Yet Another LINQ to Objects for PHP [Simplified BSD]
https://athari.github.io/YaLinqo
BSD 2-Clause "Simplified" License
441 stars 39 forks source link

Library should throw own exception types #36

Open Bilge opened 6 years ago

Bilge commented 6 years ago

It is a good practice for libraries to throw their own exception types instead of PHP/SPL types. Client applications may throw built-in exception types, but they will never throw third party library exceptions, so this practice avoids cases where trying to catch a particular type might catch the wrong exception.

Athari commented 6 years ago

The only exceptions the library throws are InvalidArgumentException and UnexpectedValueException. Both are essentially "you failed to satisfy precondition, so better check your code". While InvalidArgumentException is clearly a precondition exception and as such should never be caught specifically, the description of UnexpectedValueException in the official documentation is borderline nonsensical:

Exception thrown if a value does not match with a set of values. Typically this happens when a function calls another function and expects the return value to be of a certain type or value not including arithmetic or buffer related errors.

and there doesn't seem to be a consensus on when it's supposed to be used, so I guess I can't apply the "you shouldn't handle it" logic like for IAE, because if another library decides to use it in another way, there's no way to differentiate between mine and theirs.

Does InvalidOperationException extends LogicException sound good?

However, I don't like that the description of LogicException doesn't make much sense either:

Exception that represents error in the program logic. This kind of exception should lead directly to a fix in your code.

Is it precondition exception? Or syntax exception? In other parts of documentation they're differentiating RuntimeException as something that can't be checked at "compile time". Is it extends RuntimeException then? Ugh.

At this point, I'm considering just throwing InvalidArgumentException in all cases because that's exactly what the library means. The fact that Microsoft violated their own guidelines of InvalidOperationException usage and used the "you can't call this method when object is in this state" exception for pure static functions doesn't mean that I have to do the same.

Hmm, InvalidEnumerableException extends InvalidArgumentException maybe? Purely for ease of porting to 3.0 Just in case people actually catch UnexpectedValueException, so that they can switch 3.0 without rewriting logic.


On the inevitable "Why the heck are you so set on not catching LINQ exceptions?" question. LINQ is designed in a way that you should never catch exceptions. You should always either fix arguments, or preprocess sequence with another function, or call another function altogether.

There're a few functions which violate "pure functional programming" principles (call, each, write, writeLine) but using them is usually a bad habit, so I prefer pretending they don't exist.