Why not place exn in function signature?


#1

When I was writing java, I can place exn in function signature such as

public Scanner(File source) throws FileNotFoundException;

When I call this function without exception being catch

import java.util.Scanner;
import java.io.File;
public class ScannerFromFile {
   public static void main(String[] args) {
      Scanner in = new Scanner(new File("test.in"));
      // do something ...
   }
}

Compiler would told me:

ScannerFromFile.java:5: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
Scanner in = new Scanner(new File(“test.in”));

According to RWO, we could make the function name as sth_exn, but neither compile nor merlin would notify me to catch the exception:

open Base
open Stdio

exception Cant_alloc

let alloc size =
  if size > 10 then raise Cant_alloc
  else size


let sayhi n =
  let sz = alloc n in
  Stdio.printf "hi, %d\n" sz, Stdio.stdout

let _ =
  sayhi 1

It just works without any warning:

$ corebuild t.native
Finished, 4 targets (0 cached) in 00:00:00.
$ ./t.native
hi, 1

#2

Exceptions in ocaml are not compile-time checked, i.e. it is not a compile time error to not handle exceptions. However, if you want similar functionality then use result/option data structures. A small lib to get you started is rresult - https://github.com/dbuenzli/rresult.


#3

Thanks for your reply.

I was wondering why not add exception check in compile-time? Does that make it compile more slower or something limit by the type system?

I think such exception catch notification like java would help in large project.


#4

Hmm … not sure myself but I would guess it is the type system.

I think such exception catch notification like java would help in large project.

Indeed, I think most large ocaml projects follow the result/option way. For more about exception/error see Exceptions vs Option. Additionally, see
https://mirage.io/wiki/mirage-3.0-errors - which details how errors are handled in mirage - a large industrial ocaml code base.

I would say that ocaml way of handling error is far superior to java, both from performance and semantics point of view but ymmv.


#5

There’s also a lively discussion here:


#6

It’s ok not to handle some exceptions. We need to raise an exception if we don’t know how to handle it on a particular level. If no level has such information (about how to handle an exception) - an exception will not be handled at all and that is fine. So this is my argumentation against your case. Anyway I support warnings about such situation where there is a place for unhandled exception, cause it might be helpful for many software engineers.


#7

I believe there are difficuties with checked exceptions and higher-order functions. For example, how would you write the type for List.map if the function taken in the first parameter can throw an arbitrary exception? You’d end up having to use some generic exception annotation and lose the specific error information anyway.


#8

I think we don’t need checked exceptions in OCaml like we do in Java because OCaml supports sum types (variants, polymorphic variants).

In practice, two kinds of “errors” occur:

  • errors for which nothing specific can be done
  • specific errors which can be handled by the caller

The former benefit from the use of unchecked exceptions. Such an error could be a network failure or an assert failure. These can occur pretty much everywhere and there’s no way to recover from them. In a server loop you’d have a catch-all mechanism that logs the error, returns an error response to the client, and moves on. Nothing is done specifically to recover from one kind of error or another.

The other category of errors are those that can—and usually should—be handled by the caller of the function. In such case, it’s best to help the user to inspect and catch these errors. This is achieved well in Java with checked exceptions (your example) and it can be achieved well in OCaml by returning a value whose type is a variant e.g. Result of something_normal | Busy | Session_expired | Too_many_clouds | Angry_unicorn_crossing.

These are arguments (1) against exposing functions that raise the Not_found exception, like List.find or Hashtbl.find of the standard library and (2) against adding checked exceptions to OCaml. Use variants where you’d want checked exceptions. Use exceptions where you wouldn’t want them to appear in function signatures anyway.


#9

Great thanks , that what I need.

It seems that we could only choose one among Expressiveness(higher-order functions) and Safety(exhaustiveness checking)


#10

Note that this is not strictly true in the general sense; you can still achieve safety with sum types (variants) to represent your errors, as many have expressed in this thread. You can always replace your raise Cant_alloc expressions into Error Cant_alloc (which in turn requires you to return Ok size instead of just size) and treat it as a result type instead of checked exceptions. You would then be “forced” to handle that error on sayhi function.