Julian Coffee
Julian Coffee

Reputation: 13

Tail recursive reading of file OCaml

I write a function that print all contents of file like that

let rec print_file channel =
    try
    begin
        print_endline (input_line channel);
        print_file channel
    end
    with End_of_file -> ()

And because of print_file is last operation I was thinking that it will be optimized to regular loop. but when I had runned my program on really big file, I had got stack overflow. So I has tried to wrap input_line function to input_line_opt that don't raise exception and little change code of print_file.


let input_line_opt channel = 
    try Some (input_line channel)
    with End_of_file -> None

let rec print_file channel =
    let line = input_line_opt channel in
    match line with
        Some line -> (print_endline line; print_file channel)
        | None -> ()

And now it works like regular tail recursive function and don't overflow my stack. What difference between this two function?

Upvotes: 1

Views: 262

Answers (1)

Jeffrey Scofield
Jeffrey Scofield

Reputation: 66823

In the first example, try ... with is an operation that occurs after the recursive call to print_file. So the function is not tail recursive.

You can imagine that try sets up some data on the stack, and with removes the data from the stack. Since you remove the data after the recursive call, the stack gets deeper and deeper.

This was a consistent problem in earlier revisions of OCaml. It was tricky to write tail-recursive code for processing a file. In recent revisions you can use an exception clause of match to get a tail position for the recursive call:

let rec print_file channel =
    match input_line channel with
    | line -> print_endline line; print_file channel
    | exception End_of_file -> () 

Upvotes: 3

Related Questions