问题
I've been introduced that data, by default is immutable in F#. When we reassign value to some variable, what really happens is that it rebinds the value of variable, but setting a new value is different thing. Rebinding is called Shadowing whilst setting new value is impossible if we explicitly don't say that value of the variable is mutable.
Can anybody explain me this concept in details? what's difference between shadowing (rebinding) by
let var = "new_value"
and Setting new value like
var <- "new_value"
Is this a moment, that during rebinding we create another object and we assign that object's address to variable whereas in the second example we change the value itself? I brought that from heap/stack understanding of memory.. I may be wrong.
Thanks
回答1:
Shadowing is when you create a new binding that uses the same name as a previous binding. This "shadows" the original name, which hides it but doesn't change or replace it. Try this in FSI to see:
let foo = 42
let printFoo () =
printfn "%i" foo
printFoo() ;;
This will print:
42
val foo : int = 42
val printFoo : unit -> unit
val it : unit = ()
Then add:
// ... more code
let foo = 24
printfn "%i" foo // prints 24
printFoo ();;
This will print:
24
42
val foo : int = 24
val it : unit = ()
Note that it still prints 42 when you call printFoo()
- the function sees the original (unshadowed) binding, but the new print shows the new value.
Using <-
to mutate a value requires a mutable binding:
let mutable bar = 42
let printBar () =
printfn "%i" bar
printBar ();;
This, like above, prints 42. Note that you override the default immutable behavior here with the mutable keyword.
You then change the value within the mutable binding:
bar <- 24
printfn "%i" bar
printBar ();;
This will print 24 twice, since, unlike the shadowed version, the mutation changes the original binding. If you leave mutable
off in the original binding, you'll get an error when using <-
.
回答2:
To add on Reed Copsey's excellent answer, if you're writing a loop where you change the value of an accumulator of some sort, you'd set the original value as mutable
. For example, you can do this.
let mutable acc = 0 // declaration
for i in 1..100 do
acc <- acc + i // assignment
This is more or less equivalent to the C# code :
var acc = 0;
for (int i = 1; i <= 100; i++)
{
acc = acc + i; // assignment
// Another way to write this:
// acc += i;
}
However, in Shadowing, as in this F# snippet:
let acc = 0 // declaration
for i in 1..100 do
let acc = acc + i // another declaration only within the loop
You're not actually doing anything useful!! The second declaration has scope only within the for
loop, and it doesn't change the value of the original acc
.
A rough C# equivalent would be this:
var acc = 0; // declaration
for (int i = 1; i <= 100; i++)
{
var acc2 = acc + i; // another declaration only within the loop
}
Note that using acc
(instead of acc2
) for the inside variable wouldn't compile in C#, as it doesn't have Shadowing in this context.
The use of shadowing is that, it prevents from using the original variant in a block of code where you don't want it. So there is one less variable to worry about.
回答3:
Whenever I wonder what actually is happening I use tools like ILSpy
For example:
let f () =
let x = Dictionary<int, string> ()
let mutable x = ResizeArray<int> 16
x <- ResizeArray<int> 16
Using ILSpy to decompile it in C# code it becomes:
public static void f()
{
Dictionary<int, string> x = new Dictionary<int, string>();
List<int> x2 = new List<int>(16);
x2 = new List<int>(16);
}
Here it's more obvious what the difference between shadowing and setting.
Shadowing x
creates a new variable with name x2
.
Setting is normal assignment.
来源:https://stackoverflow.com/questions/39644473/shadowing-vs-setting-value-in-f