I know that variables in F# are immutable by default. But, for example in F# interactive:
> let x = 4;;
val x : int = 4
> let x = 5;;
val x : int = 5
> x;;
val it : int = 5
>
So, I assign 4 to x, then 5 to x and it's changing. Is it correct? Should it give some error or warning? Or I just don't understand how it works?
Here's a minimal example illustrating identifier "shadowing" (i.e. hiding) in F#:
Your example is actually a bit more complex, because you are working in the F# Interactive (FSI). FSI dynamically emits code that looks something like the following in your example:
You're not assigning 5 to
x
, you are defining a new variable.The following example shows that there are two distinct variables. (It also shows that you can "access" the old x if it is in a closure, used by another function):
yields
as you see, both calls to f use the first variable
x
. The definitionlet x = 0;;
defines a new variablex
, but does not redefinesf
.When you write
let x = 3
, you are binding the identifierx
to the value3
. If you do that a second time in the same scope, you are declaring a new identifier that hides the previous one since it has the same name.Mutating a value in F# is done via the destructive update operator,
<-
. This will fail for immutable values, i.e.:To declare a mutable variable, add
mutable
afterlet
:But what's the difference between the two, you might ask? An example may be enough:
Despite the appearances, this is an infinite loop. The
i
declared inside the loop is a differenti
that hides the outer one. The outer one is immutable, so it always keeps its value0
and the loop never ends. The correct way to write this is with a mutable variable:x
is not changed, it's just hidden by next declaration. For example: