Functions for working with computations which may fail.
A Result is used to represent a computation which may fail.
A Result is a variant, which has a constructor for successful results (Ok 'ok), and one for unsuccessful results ((Error 'error)).
type ('ok, 'error) t =
| Ok of 'ok
| Error of 'error
Here is how you would annotate a Result variable whose Ok variant is an integer and whose Error variant is a string:
let ok: (int, string) Result.t = Ok 3
let error: (int, string) Result.t = Error "This computation failed!"
Note The 'error case can be of any type and while string is very common you could also use:
string List.t to allow errors to be accumulated
exn, in which case the result type just makes exceptions explicit in the return type
A variant or polymorphic variant, with one case per possible error. This is means each error can be dealt with explicitly. See this excellent article for more information on this approach.
If the function you are writing can only fail in a single obvious way, maybe you want an Option instead.
A function alternative to the Ok constructor which can be used in places where the constructor isn't permitted such as at the of a Fun.(|>) or functions like List.map.
Examples
String.reverse "desserts" |> Result.ok = Ok "stressed"
List.map [1; 2; 3] ~f:Result.ok = [Ok 1; Ok 2; Ok 3]
A function alternative to the Error constructor which can be used in places where the constructor isn't permitted such as at the of a Fun.pipe or functions like List.map.
Useful when you want to perform some side effect based on the presence of an Ok like logging.
Note if you need access to the contained value rather than doing Result.is_ok followed by Result.unwrap_unsafe its safer and just as convenient to use pattern matching directly or use one of Result.and_then or Result.map
Returns the first argument if it is_error, otherwise return the second argument.
Unlike the Bool.(&&) operator, the and_ function does not short-circuit. When you call and_, both arguments are evaluated before being passed to the function.
Examples
Result.and_ (Ok "Antelope") (Ok "Salmon") = Ok "Salmon"
Return the first argument if it is_ok, otherwise return the second.
Unlike the built in || operator, the or_ function does not short-circuit. When you call or_, both arguments are evaluated before being passed to the function.
Result.combine results takes a list of Result values. If all the elements in results are of the form Ok x, then Result.combine creates a list xs of all the values extracted from their Oks, and returns Ok xs
If any of the elements in results are of the form Error err, the first of them is returned as the result of Result.combine.
Examples
Result.combine [Ok 1; Ok 2; Ok 3; Ok 4] = Ok [1; 2; 3; 4]
Result.combine [Ok 1; Error "two"; Ok 3; Error "four"] = Error "two"
let reciprical (x:float) : (float, string) Result.t = (
if (x = 0.0) then
Error "Divide by zero"
else
Ok (1.0 /. x)
)
let root (x:float) : (float, string) Result.t = (
if (x < 0.0) then
Error "Cannot be negative"
else
Ok (Float.square_root x)
)
In functions that make heavy use of Results operators can make code significantly more concise at the expense of placing a greater cognitive burden on future readers.
Result.pp err_format ok_format dest_format result "pretty-prints" the result, using err_format if the result is an Error value or ok_format if the result is an Ok value. dest_format is a formatter that tells where to send the output.
let good: (int, string) Result.t = Ok 42 in
let not_good: (int, string) Tablecloth.Result.t = Error "bad" in
Result.pp Format.pp_print_int Format.pp_print_string Format.std_formatter good;
Result.pp Format.pp_print_int Format.pp_print_string Format.std_formatter not_good;
Format.pp_print_newline Format.std_formatter ();
(* prints <ok: 42><error: bad>*)