I have read that values in F# are immutable. However, I have also come across the concept of redefining value definitions, which shadow the previous ones. How is this different from a mutable value ? I ask this not just as a theoretical construct, but also if there is any advice on when to use mutable values and when to redefine expressions instead; or if someone can point out that the latter is not idiomatic f#.
Basic example of redefinition:
let a = 1;;
a;; //1
let a = 2;;
a;; //2
Update 1:
Adding to the answers below, the redefinition in Fsharp interactive at top level is only allowed in different terminations. The following will throw up an error in fsi as well:
let a = 1
let a = 2;;
Error: Duplicate definition of value 'a'
On the other hand, redefinition is allowed in let bindings.
Update 2: Practical difference, closures cannot work with mutable variables:
let f =
let mutable a = 1
let g () = a //error
0
f;;
Update 3:
While I can model side effects with refs, eg:
let f =
let a = ref 1
let g = a
a:=2
let x = !g + !a
printfn "x: %i" x //4
f;;
I can't quite see a practical difference between redefinition and using the mutable keyword, besides the difference in usage with closures, eg:
let f =
let a = 1
let g = a
let a = 2
let x = g + a
printfn "x: %i" x //3
f;;
vs
let f =
let mutable a = 1
let g = a
a <-2
let x = g + a
printfn "x: %i" x //3
f;;
Another line of thought: I'm not sure how to work with threads, but (a) can another thread can mutate the value of a mutable variable within a let binding and (b) can another thread rebind/redefine a value name within a let binding. I am certainly missing something here.
Update 4: The difference in the last case is that the mutation would still happen from a nested scope, whereas a redefinition/rebinding in the nested scope will 'shadow' definition from the external scope.
let f =
let mutable a = 1
let g = a
if true then
a <-2
let x = g + a
printfn "x: %i" x //3
f;;
vs
let f =
let a = 1
let g = a
if true then
let a = 2
printfn "a: %i" a
let x = g + a
printfn "x: %i" x //2
f;;
'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:
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:
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.:
you will indeed get a compiler error.