Let expressions are like let definitions, but they're expressions!
let a = 0 in a;;
- : int = 0
The in keyword makes this a let expression. All it does is replace all instances of a in the expression following in with the value assigned to a.
let b = 1 in 2 * b;;
- : int = 2
Here, b was bound to 2, so OCaml evaluated 1 * 2, which is 2.
b;;
File "[3]", line 1, characters 0-1:
1 | b;;
^
Error: Unbound value b
See how b doesn't exist outside that expression? That's because it only is bound in that one expression.
let c = 3 in (let d = 4 in c + d);;
- : int = 7
This is pretty simple! First, d is bound to 4, and the expression c + 4 is returned from the inside let expression, then c is bound to 3, and 3 + 4 is returned, so, 7!
What if we use the same binding twice?
let e = 5 in (let e = 7 in e);;
File "[5]", line 1, characters 4-5:
1 | let e = 5 in (let e = 7 in e);;
^
Warning 26: unused variable e.
- : int = 7
Wait! What's happening?