When we say a variable is immutable, we mean that its own value cannot be changed. What you're showing there with
val a = 3
val a = a+1
is: the new value of a is simply "shadowing" the old value of a. a is just a name that is bound to 3, and in the second line, it's bound to 4. The old value of a still exists, it's just inaccessible.
This can be seen more apparently if you're using some sort of data structures. There's no mutator methods like you see in many other languages. For example, if you have a list val L = [1,2,3], then there's no way to change the first value in L. You would have to shadow L altogether, and create a new list to shadow the old one.
So, every time you bind a new value declaration it creates a new environment with all the current name/value bindings. None of these bindings can be changed, they're simply shadowed.
ais a new variable which happens to be called the same as the previous one and therefore masks it in a nested context. Compiler will rewrite this code anyway as something likelet a000 = 3 in let a001 = a000+1 in ...let a = 1 in let b = (let a = a+1 in a /* a is 2 */) in a /* b is forgotten, returning the original a */let x = a in ...is expanded into((fun x -> ...) a), i.e.,xis a function argument name here.