I'm solving problems posted in one of the OCaml tutorials. One of them is to write a function that will calculate the length of the list. It should be tail-recursive.
The naive, non tail-recursive solution is:
let rec length_naive xs = (** Not tail recursive *)
match xs with
| [] -> 0
| _ :: xs -> 1 + length_naive xs;;
(length_naive [@tailcall]) [1, 2, 3, 4];;
let one_hundred_million = 100000000;;
let non_tail_result = length_naive(List.init one_hundred_million (Fun.id));;
print_endline("Non-tail-recursive result: " ^ string_of_int(non_tail_result));;
Compiling and running this works exactly as expected. The warning (due to @tailcall) is printed, and the execution fails due to the stack getting overfown:
$ ocamlc lib/so.ml
File "lib/so.ml", line 14, characters 0-39:
14 | (length_naive [@tailcall]) [1, 2, 3, 4];;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Warning 51 [wrong-tailcall-expectation]: expected tailcall
$ ./a.out
Fatal error: exception Stack_overflow
Now time for my attempt at writing a tail recursive version:
let length xs =
let rec f xs len =
match xs with
| [] -> len
| _ :: xs -> f xs (len + 1)
in
f xs 0;;
(length [@tailcall]) [1, 2, 3, 4];;
let one_hundred_million = 100000000;;
let tail_result = length(List.init one_hundred_million (Fun.id));;
print_endline("Tail recursive result: " ^ string_of_int(tail_result));;
Compiling and running it:
$ ocamlc lib/so.ml
File "lib/so.ml", line 15, characters 0-33:
15 | (length [@tailcall]) [1, 2, 3, 4];;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Warning 51 [wrong-tailcall-expectation]: expected tailcall
$ ./a.out
Tail recursive result: 100000000
So it works, stack overflow exception didn't get thrown. However, compiler warns about the function not being tail recursive. My questions are:
- if this function is tail-recursive, why is compiler printing a warning?
- if this function is not tail-recursive, why is that?
I'm using OCaml compiler v 4.14.0.