A colleague reviewed my code and asked why I was using the following pattern:
type MyType() =
member this.TestMethod() =
let a = 1
// do some work
let a = 2 // another value of a from the work result
()
I told him that that was a bad pattern and my intent was to use let mutable a = ...
. But then he asked why at all it is possible in F# class to have such sequential bindings, while it is not possible in a module or an .fsx
file? In effect, we are mutating an immutable value!
I answered that in a module or an .fsx file a let binding becomes a static method, therefore it is straightforward that the bindings with the same name will conflict in the same way as two class properties with the same name do. But I have no idea why this is possible inside a method!
In C#, I have found useful to scope variables, especially in unit tests when I want to make up some different values for test cases by just copy-pasting pieces of code:
{
var a = 1;
Assert.IsTrue(a < 2);
}
{
var a = 42;
Assert.AreEqual(42, a);
}
In F#, we could not only repeat the same let bindings, but change an immutable one to a mutable one and then mutate it later the usual way:
type MyType() =
member this.TestMethod() =
let a = 1
// do some work
let a = 2 // another value of a from the work result
let mutable a = 3
a <- 4
()
Why we are allowed to repeat let bindings in F# methods? How should I explain this to a person who is new to F# and asked "What is an immutable variable and why I am mutating it?"
Personally, I am interested in what design choices and trade offs were made to allow this? I am comfortable with the cases when the different scopes are easily detectable, e.g. when a variable is in constructor and then we redefine it in the body, or when we define a new binding inside a for
/while
loops. But two consecutive bindings at the same level are somewhat counter-intuitive. It feels like I should mentally add in
to the end of each line as in the verbose syntax to explain the scopes, so that those virtual in
s are similar to C#'s {}