Login

Errors

Bad list syntax -> bad ocamlformat -> bad list

Consider this example of dependency injection via module (inspired by why ocaml):

module type IO = sig
  val print_endline : string -> unit
  val read_line : unit -> string
end

let program (module Handler : IO) =
  let () = Handler.print_endline "Hello World" in
  let () = Handler.print_endline "What is your name?" in
  let name = Handler.read_line () in
  Handler.print_endline ("Hello " ^ name)

let () =
  let output = ref [] in
  let module TestIO = struct
    let print_endline s = output := s :: !output
    let read_line () = "Test user"
  end in
  program (module TestIO);
  assert (!output = ["Hello Test user", "What is your name?", "Hello World"])

The last line exhibits an easy error in OCaml if you're very used to other languages: that's not a list of three strings, but a list of one string * string * string tuple, making it a compile-time error as that's incompatible with !output:

19 |   assert (!output = ["Hello Test user", "What is your name?", "Hello World"])
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type 'a * 'b * 'c
       but an expression was expected of type string

OK, so you open the file back up, reflexively ocamlformat it, and then change commas to semicolons. Resulting in this line:

  assert (!output = [("Hello Test user"; "What is your name?"; "Hello World")])

Which is no longer an error! It compiles! But suspiciously, it compiles with some warnings, and the assertion fails:

$ ocaml o17.ml 
File "./o17.ml", line 19, characters 22-39:
19 |   assert (!output = [("Hello Test user"; "What is your name?"; "Hello World")])
                           ^^^^^^^^^^^^^^^^^
Warning 10 [non-unit-statement]: this expression should have type unit.

File "./o17.ml", line 19, characters 41-61:
19 |   assert (!output = [("Hello Test user"; "What is your name?"; "Hello World")])
                                              ^^^^^^^^^^^^^^^^^^^^
Warning 10 [non-unit-statement]: this expression should have type unit.

Exception: Assert_failure ("./o17.ml", 19, 2).

ocamlformat added parentheses to make it clear that we had a tuple, but parentheses are not actually a syntax for tuples - it's commas that create tuples. The result is instead equivalent to begin "Hello Test User"; ... end, so what's created is a list of a single string, and the first two strings are discarded.

With those parentheses removed, the assertions pass.