In a nutshell a functional value conforming to this recommendation raises only
Assert_failure, to respectively denote a misuse of the function by the client and an unexpected internal error. If the function may return a value or an error it does so by returning a polymorphic variant with a
`Ok case to return the value and an
`Error case for errors. If the function returns either a value or no result it uses an
This recommendation says nothing about the usage of exceptions in the implementation of the function. Neither does it say something about clients, if they want to use an exception after an error occured they can define their own exceptions.
A functional value conforms to this recommendation if it guarantees the following points.
Invalid_argumentto signal a programming error made by the client.
Assert_failureto signal an unexpected internal error.
Example for (1) : negative character positions in string functions.
[ `Ok of ... | `Error of ... ](any type can fill the ellipsis dots). Example :
val parse_int : string -> [ `Ok of int | `Error of [`Syntax | `Overflow ]]
optiontype. Example :
val input_line : in_channel -> string option
A module conforms to this recommendation if each of the functional values of its signature conforms to the recommendation.
Suggestions are not part of the recommendation. They are here to provide guidelines about how and when to apply the recommendation.
In the following situations :
Declare a separate error type for the cases. Example :
type error = [ `First_case | ... ] val parse : in_channel -> [ `Ok of t | `Error of (int * int) * error ]
In the the following situations :
Do not follow the recommendation and use an exception to report errors.
Usage of the
Failure exception in the standard library for unexceptional error cases was acknowledged by Xavier Leroy as being an unsatisfactory legacy solution. In the same discussion Yaron Minsky suggested to use polymorphic variants as a lightweight but explicit mechanism to signal errors. These ideas already appeared in this thread of discussion started by Brian Hurt.
The recommendation was updated to allow Assert_failure. -- Daniel
See this link -- Daniel
Ok I refrained to add comments about catching the allowed exceptions. -- Daniel
if (open(...) == -1) ...; if (ioctl(...) == -1) ...; if (read(...) == -1) ...This approach would make the OCaml code look just as bad. -- Eric Cooper
I agree with this. I added in the suggestion part, cases where the recommendation should not be followed. -- Daniel
Actually, Unix is expected to mimick the libc and have a low-level approach. On the other hand, I would expect those higher-level functions of Pervasives and Sys implementing the same set of features as Unix to also have a higher-level of error-management. -- David Teller
I don't understand your comments about using polymorphic variants where did you get that from ? There's a whole chapter about them in the ocaml manual. -- Daniel
Looking back at the manual again, I guess the warning isn't overly present. It was probably just me reading "weaker type discipline" in the context of "typing is why we use an ML," and inferring that polymorphic variants are best avoided (at least when regular variants can do the job). -- Michael
let safe_div x = function | 0. -> `Div_by_zero | y -> `Ok (x /. y) let idiv x y = match safe_div (float_of_int x) (float_of_int y) with | `Success x -> `Ok x | _ -> `Error
This compiles cleanly even with all the warnings...
However in the particular case of Div_by_zero, that's rather a programming error, so a Invalid_argument would be better. -- Nicolas Pouillard
Regarding your example this is why the recommandation says you should return `Value and neither `Success nor `Ok or your own fancy tag. But I agree polymorphic variants are less strict -- Daniel
type ('a,'b) mayFail = Ok of 'a | Error of 'b. -- Till Varoquaux I also prefer using simple things like Either instead of polymorphic variants, when that's sufficient. -- Nicolas Pouillard
The advantage of this recommendation is that we don't need any new type definition in the standard library or external dependency to fullfill it -- Daniel