Advantage of `let rec` bindings vs regular `let` bindings in OCaml
ocaml
Solution
As I stated already in my comment, it seems that by using `let rec`, you avoid creating more closures. To inspect this, I created two slightly different files shown here:
`test1.ml` has the "usual" way without `let rec`:
let test1 x =
let x = 5 in
let w () = x + 1 in
let n () = x + 1 in
w () + n ()
On the other hand, `test2.ml` uses `let rec`:
let test2 x =
let x = 5 in
let rec w () = x + 1
and n () = x + 1 in
w () + n ()
I then `ocamlc -dinstr`'d both files (i.e. I generated bytecode for both files) and obtained the following:
For `test1.ml`, I got:
branch L2
L3: envacc 1
offsetint 1
return 1
L4: envacc 1
offsetint 1
return 1
L1: const 5
push
acc 0
closure L4, 1
push
acc 1
closure L3, 1
push
const 0a
push
acc 1
apply 1
push
const 0a
push
acc 3
apply 1
addint
return 4
L2: closure L1, 0
push
acc 0
makeblock 1, 0
pop 1
setglobal Closuretest!
The file `test2.ml` resulted in the following:
branch L2
L3: envacc 3
offsetint 1
return 1
L4: envacc 1
offsetint 1
return 1
L1: const 5
push
acc 0
closurerec 3 4, 1
const 0a
push
acc 1
apply 1
push
const 0a
push
acc 3
apply 1
addint
return 4
L2: closure L1, 0
push
acc 0
makeblock 1, 0
pop 1
setglobal Closuretest2!
From a Caml Virtual Machine — Instruction set Document (unfortunately, I don't know if it even is the official one, but it looks reasonable), it seems that the instructions `closure` and `closurerec` generate a closure on the stack. As you can see, the bytecode for `test1.ml` generates 3 closures in total, while `test2.ml` only generates two closures (one via `closurerec`).
I am no assembly guru, but you can test `ocamlopt -S filename.ml`, so that the compiler leaves (and does not delete) the assembly (then in `filename.s`), where you can possibly spot analogous differences, as well.
Problem
This question is sort-of a follow-up question to this SO question: How to make a covariant observable in OCaml The accepted answer's author has (casually) noted that using `let rec` to bind two independent values would be more "economical" than two separate `let` bindings. ``` let make x = let queue = Queue.create () in let obj = x in let watch f = Queue.add f queue in let notify () = Queue.iter (fun f -> f x) queue in { obj; watch; notify; } ``` vs ``` let make x = let queue = Queue.create () in let obj = x in let rec watch f = Queue.add f queue and notify () = Queue.iter (fun f -> f x) queue in { obj; watch; notify; } ``` Was his statement correct? If so, why is the second version more "economical"?