'I'm not sure I agree with some of the answers given.
The following compiles and executes perfectly both in FSI and in a real assembly:
let TestShadowing() =
let a = 1
let a = 2
a
But it's important to understand that what is going on is not mutation, but shadowing. In other words the value of 'a' hasn't been reassigned. Another 'a' has been declared with its own immutable value. Why does the distinction matter? Consider what happens when 'a' is shadowed in an inner block:
let TestShadowing2() =
let a = 1
printfn "a: %i" a
if true then
let a = 2
printfn "a: %i" a
printfn "a: %i" a
> TestShadowing2();;
a: 1
a: 2
a: 1
In this case the second 'a' only shadows the first one while the second one is in scope. Once it goes out of scope the first 'a' pops back into existence.
If you don't realize this it can lead to subtle bugs!
Clarification in the light of Guy Coder's comment:
The behaviour I decribe above occurs when the redefinition is within some let binding (i.e. within the TestShadowing() functions in my examples). This I would say is by far the most common scenario in practice. But as Guy says, if you redefine at the top level, e.g.:
module Demo =
let a = 1
let a = 2
you will indeed get a compiler error.