2014-05-26 18:34 GMT+02:00 Daniel Bünzli <daniel.buenzli@erratique.ch>:
Le lundi, 26 mai 2014 à 18:02, Philippe Veber a écrit :
> Thanks! BTW core still uses exceptions. Is there an explicit rule as to how to decide between Result type or exceptions. For instance, why not write the Array.create function like this:
>
> val create : int -> 'a -> 'a array Or_error.t
>
> where create fails for a negative integer?
Because that would be utterly annoying. You need to make the following distinctions:

* Programming errors, for contracts with the programmer that cannot be enforced through types. For that raises Invalid_argument if the contract is violated. Invalid_argument is not supposed to be handled, it denotes an API misuse, like calling Array.create with a negative integer.

* Exceptional errors, for errors that the programmer is unlikely to handle at all (e.g. out of memory). For that raise a custom exception. This should occur very rarely, you are unlikely to ever define one such exception.

* Non-exceptional errors, errors that the programmer will have to handle (e.g. failing to connect a socket), for that do not use a custom exception but use variants or options types.

In general if you write libraries it’s better to err on the side of exceptionless design: never use exceptions beyond Invalid_argument (and especially never use Not_found or Failure).

Thanks Daniel, this is a useful summary. A couple of questions/remarks:
- I observed in many libraries (including Xmlm) that people tend to define custom exceptions to signal parsing errors. According to the rules you provide, parsers should return variants, right?
- as Ben noted earlier, I think point 2 would better be formulated as errors that can virtually appear anywhere and/or a programmer cannot do much about (as you say)
- The adjective "exceptional" is a bit misleading IMO, in particular I guess it should not be understood as "not frequent". Rare failures can be very harmful and should be given extra-care since they are not easy to provoke and debug. Did you mean more like "very abnormal" and so difficult to recover?
- Another criterion I like is that if checking the precondition of a function f basically amounts (in terms of complexity/algorithm) to calling f, then f shouldn't raise an exception for this precondition. If the precondition is difficult to check, a user is likely not to check it:
    - List.find assumes the searched element is in the list, but checking that is precisely what the function is about.
    - Connecting to a socket assumes it is up and available, but checking that without doing the connection is meaningless because you are not sure the socket will be available just after the check
    - in a parser you won't know if the input is syntactically correct before actually performing the parsing.

 
Leave exception definition/usage at the discretion of the user (if he wishes to shoot himself in the foot).

Having no experience on large projects, it is not clear to me why exceptions are considered so dangerous. One point I see is that when you decide to handle errors with a try ... with expression, there is no exhaustivity check to ensure you handled all possible cases, and that you have to rely on (possibly incomplete) documentation and reasonning to convince yourself you forgot nothing. Another related one is that there is no automatic analysis to show exceptions thrown by a function in the documentation. These are of course already strong reasons why not to use too many exceptions (and were the motivation for my initial question). Do you see other reasons?

 Anyway, thanks everyone, this has been very informative to me!


Best,

Daniel