Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shadowing vs. Setting value in F#

I was taught, 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 don't say explicitly, that value of the variable is mutable.

Can anyone explain to me this concept in a bit more details?

What is the difference between shadowing (rebinding):

let var = "new_value"

and setting a new value, as:

var <- "new_value"

Is this a moment, that during rebinding we create another object and we assign that object's address to the variable, whereas in the second example we change the value itself? I brought that from heap/stack concept.. but I may be wrong.

Thanks.

like image 346
Giorgi Tsiklauri Avatar asked Sep 22 '16 16:09

Giorgi Tsiklauri


3 Answers

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 <-.

like image 149
Reed Copsey Avatar answered Oct 25 '22 03:10

Reed Copsey


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.

like image 42
asibahi Avatar answered Oct 25 '22 02:10

asibahi


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.

like image 43
Just another metaprogrammer Avatar answered Oct 25 '22 02:10

Just another metaprogrammer