Login

Caveats

Caveats elsewhere:

  1. OCaml: the bugs so far (source of "*1" items)
  2. 7 OCaml Gotchas

No warning about leading zeroes

(* o23.ml *)
let () =
  (* set my perms to u=rwx, g/o nothing *)
  Unix.chmod Sys.argv.(0) 0700
$ ocamlfind ocamlc -linkpkg -package unix o23.ml 
$ ls -ld a.out|cut -d' ' -f1
-rwxr-xr-x.
$ ./a.out 
$ ls -ld a.out|cut -d' ' -f1
--w-rwxr-T.

0700 is decimal 700, not the octal number mapping to binary 111_000_000. Octal 700 is written 0o700.

# let huh = 0700 in huh=700, huh, 0o700, 0b111000000;;
- : bool * int * int * int = (true, 700, 448, 448)

No warning about uncalled unit functions in a sequence

(* o24.ml *)
let trace fn =
  print_endline "START";
  fn ();
  print_endline "END"

let () = trace (fun () -> Printf.printf "Hello %s!\n")
$ ocaml o24.ml 
START
END
$ ocaml -strict-sequence o24.ml 
File "./o24.ml", line 6, characters 26-53:
6 | let () = trace (fun () -> Printf.printf "Hello %s!\n")
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type string -> unit
       but an expression was expected of type unit
Hint: This function application is partial, maybe some arguments are missing.

That printf, by the %s, is expecting another argument. Merlin helpfully typed the function in the last line as unit -> string -> unit when I couldn't immediately see the problem.

OCaml has tagged integers, 1 bit shorter than you expect

Like typical Common Lisp implementations, and also to give the GC a fast way to distinguish pointers from other data, OCaml int has one less bit available to the programmer than the machine integer has. So on a 64-bit machine,

# max_int, Int64.(div max_int 2L |> to_int) = max_int;;
- : int * bool = (4611686018427387903, true)

mod is truncating (like in C) instead of Euclidean (like in Perl, Python)

$ perl -le 'print -13%100'
87
$ ocaml -e 'Printf.printf "%d\n" ((-13) mod 100)'
-13

Euclidean definition from discuss.ocaml.org:

let (%) x y =
  let z = x mod y in
  if z < 0 then z + y else z

C-resembling equality ops == != are physical, not structural

# let a = "hello" let b = a ^ "";;
val a : string = "hello"
val b : string = "hello"
# a = b;;
- : bool = true
# a == b;;
- : bool = false
# a <> b;;
- : bool = false
# a != b;;
- : bool = true

Evaluation order of arguments to constructors, as to functions in many languages, is undefined

Despite being very familiar with this error from C, I was slow to recognize it in this code:

# let rec lines () = try read_line () :: lines () with End_of_file -> [];;
val lines : unit -> string list = <fun>
# lines ();;
Stack overflow during evaluation (looping recursion?).

It's not defined which argument to :: is evaluated first, and on my machine the tail argument was evaluated first, similarly to

let rec lines () =
  let tl = lines () in
  try read_line () :: tl with End_of_file -> []

Also true of let ... and

The second of [%defer ...]; [%defer ...] is an uninterpreted extension 'defer'.

(* this works: *)
  [%defer Unix.close client];
  ();
  [%defer Unix.shutdown client Unix.SHUTDOWN_ALL];

(* and this doesn't! *)
  [%defer Unix.close client];
  [%defer Unix.shutdown client Unix.SHUTDOWN_ALL];
(*  ^^^^^
Error: Uninterpreted extension 'defer'.*)

This is an unfortunate limitation of ppx, or perhaps just of ppx_defer: it needs a normal following expression to latch onto, and the second 'defer' doesn't cut it.