Shadowing vs. Setting value in F#

Deadly 提交于 2019-12-08 16:09:56

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!