Chiming in a bit late, but I pretty much love Swift’s way of handling checked exceptions. For me, they strike the perfect balance between providing a sanity check when calling an exception-raising function, without colouring the calling side too much. I think this could be a nice fit for OCamls exceptions and effects as well.
This is how Swift does error-handling.
// Throwing functions need to have the throws keyword
func someThrowingFunction() -> throws {
let ratings = [1, 2, 3, 2, 2, 1]
// summarize throws an error, which has to be acknowledged by a leading try.
// This error "bubbles up" the call stack automatically.
try summarize(ratings)
}
func someNonThrowingFunction() {
let ratings = [1, 2, 3, 2, 2, 1]
// summarize throws an error, which has to be acknowledged by a leading try.
// A do try block catches the error, like you know it from other languages.
do {
try summarize(ratings)
catch {
print("Catched an error")
}
}
func someNonThrowingFunction() {
let ratings = [1, 2, 3, 2, 2, 1]
// summarize throws an error, which has to be acknowledged by a leading try.
// foo is an optional and when summarize throws, foo is empty.
let foo = try? summarize(ratings)
guard foo != nil else {
print("Summarize failed and foo has no value")
}
}
func someNonThrowingFunction() {
let ratings = [1, 2, 3, 2, 2, 1]
// summarize throws an error, which has to be acknowledged by a leading try.
// If summarize throws, the runtime will panic. Just like uncatched exceptions.
// This is super discouraged and is basically the same as Rust's panic!().
let foo = try! summarize(ratings)
print("The program did not panic and foo has the return value")
}
As you can see, the caller has to acknowledge the error in each case, but it can easily be propagated to the callee. For Ocaml the easiest thing would be to change the semantics of the try
keyword, but that would not be backwards compatible.
This is how it works now:
let rec sum_up acc =
let l = input_line stdin in
acc := !acc + int_of_string l;
sum_up acc
let run =
let r = ref 0 in
sum_up r
(* This can maybe? raise an exception or cause an effect *)
let _ =
try run with
| End_of_file -> print_endline "EOF encountered"
And this would be with a mandatory try
.
let rec sum_up acc =
let l = input_line stdin in
acc := !acc + int_of_string l;
sum_up acc
let run =
let r = ref 0 in
try sum_up r
(* I acknowledge this can definitely raise an exception or cause an effect which propagates *)
let _ =
try run with
| End_of_file -> print_endline "EOF encountered"
This is however only one possibility I came up with. The alert feature mentioned my @yawaramin can potentially introduce the same effect without breaking backwards compatibility and keeping the semantics I mentioned above. Honestly I am a very irregular OCaml user and consider myself as very unseasoned, as I mostly use Elixir, Swift, Rust, JS and Go to various degrees. I am just learning about effect handlers and this was one pain point I encountered with booth effect handlers and exceptions. Therefore this is a very high level take on the matter