(OCaml 4.02 has branched, which makes it a good time to stop and take a look at what to expect for this release. This is part of a series of posts where I'll describe the features that strike me as notable. This is part 3. You can also check out parts 1 and 2.)
This one is a modest improvement, but a nice one.
Here's a simple bit of code for reading a file line-by-line in OCaml.
But the above code has a problem: it's not tail recursive, because the recursive call to [loop] is within the exception handler, and therefore not a tail call. Which means, if you run this on a sufficiently large file, it will run out of stack space and crash.
But there's a standard way around this problem, which is to wrap just the input_line call with
try_with, and then pattern match on the result. That would normally be done like this:
This is an OK solution, but it has some warts. In particular, there's the extra option that gets allocated and immediately forgotten, which can be problematic from a performance perspective. Also, the nesting of the try/with within the match is a bit on the ugly side.
That's where handler-case comes in. Essentially, in 4.02 the
match statement and the
try-with statement have been combined together into one. Or, more precisely, the match syntax has been extended to allow you to catch exceptions too. That means you can rewrite the above as follows.
This is both more concise and more readable than the previous syntax. And the call to loop is tail-recursive, as one would hope.
(If this isn't obvious, while the above is a good example, it's not what you'd write to solve this problem in practice. Instead, you might use Core's
In_channel.fold_lines, as follows:
- let read_lines inc =
- In_channel.fold_lines inc ~init: ~f:(fun l x -> x :: l)
- |> List.rev
Or you could just call