Why does F# Interactive behave differently than compiler with regards to immutable value definition?

StackOverflow https://stackoverflow.com/questions/20327112

  •  07-08-2022
  •  | 
  •  

Question

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.

Was it helpful?

Solution

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

module FSI_1 = 
    let x = 1

open FSI_1 //FSI_1.x is now bound to 1 and available at the top level

FSI Submission #2: let x = 2;;

module FSI_2 = 
    let x = 2

open FSI_2 //FSI_1.x is now shadowed by FSI_2.x which is bound to 2 and available at the top level

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 pattern FSI_####. 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#24

The 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:

> let x = 1;; //our original definition of x
val x : int = 1

> let f () = x;; //capture x
val f : unit -> int

> let x = 2;; //shadow our original definition of x
val x : int = 2

> f();; //returns the original x value, which is still 1 rather than 2
val it : int = 1
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top