I think it is useful to understand the syntax without light. Let me translate:
1st version (let it bind expressions)
let samplefn() = let z = 2 in let z = z * 2 in printfn "hi";; samplefn();;
It is important to understand that all let bindings at the top level are actually an expression of the form let <variable> = <expression1> in <expression2> , where <variable> bound to the result <expression1> in the new area <expression2> , and <expression2> is the return value of the entire expression. The light syntax makes you believe that such let bindings are variable assignments / operators, when in fact it is true that almost everything in F # is an expression.
Perhaps the following illustrates this more clearly:
let a = (let b = 3 in b + 2)
Second version (top level bindings)
let z = 2;; let z = z * 2;; printfn "Error: Duplicate definition of value z";;
Top-level let-bindings end with ;; , indicating the completion of what can be considered a statement. The upper level represents a single area, and here we get an error for trying to bind z twice to the same area. Whereas, using the let bindings expression form in Example 1, we bind z again for each sub-area in the expression chain. Note that we could do something like this at the top level:
let z = (let z = 2 in z * 2);;
source share