In reading John Palmer's answer to What is the difference between mutable values and immutable value redefinition?, John notes that
This sort of redefinition will only work in fsi.
In working with F# Interactive (fsi) I guess I subconsciously knew it, but never paid attention to it.
Now that it is apparent, why the difference?
More specifically please explain how the internals are different between fsi and the compiler such that this occurs by design or result of differences?
If the answer can elaborate on the internal structures that hold the bindings that would be appreciated.
The semantics are consistent with the way FSI compiles interactive submissions to an FSI session: each interactive submission is compiled as
module
which is open to subsequent interactive submissions.The following is close to what FSI actual does and illustrates how let binding shadowing works across interactive submissions:
FSI Submission #1:
let x = 1;;
FSI Submission #2:
let x = 2;;
You can see the actual details how how the dynamic FSI assembly is compiled by using reflection on the
FSI_ASSEMBLY
assembly within the FSI app domain. Each interactive submission is literally emitted as a module (.NET class) with the naming patternFSI_####
. FsEye uses these facts to discover the state of FSI top-level bindings: https://code.google.com/p/fseye/source/browse/tags/2.0.1/FsEye/Fsi/SessionQueries.fs#24The key takeaway in regard to @JohnPalmer's answer is that top-level FSI definitions cannot be mutated, when they are "redefined" they are merely being shadowed. We can show this as follows: