The main difference is that in
let x = [1; 2; 3] in
let y = [] in
x @ y
the variable x
could be used in the definition of y
, e.g., you can write let y = [x]
, while in the latter example, y
is defined in parallel with x
.
The latter style (with and
) lets you state explicitly that the computation of y
doesn’t depend on x
, which is always good. Concerning the compiler, it can potentially type parallel bindings faster, but type-checking is so fast in OCaml so you wan’t notice any difference unless you have thousands of nested lets (e.g., when your code is generated).
Concerning the mutually recursive case, it is again not because the type-checking is expensive. OCaml can easily cope with more mutually recursive functions than a mere human can write. We can imagine, that mutually recursive functions can prevent some compiler optimizations, like inlining, but this is natural. Of course, using rec
in cases when it is not needed is a bad idea, as you can accidentally make non-recursive call recursive and in general increase confusion for someone who will read your code. That’s why there is a special warning for a non-recursive definition that is marked as recursive.
The only complexity of mutually recursive definitions is that the typing scope is extended to all bindings that compise the definition. So when you change something in one definition, the error may pop up miles away in a completely different definition. In other words, the larger the scope the harder it is to reason, at least for a human. It is not a problem for the compiler though.