问题
If I write the following F# code, the compiler issues an error.
let a = 123
let a = 123
The error produced is:
error FS0037: Duplicate definition of value 'a'
If I write the same code in a function like this:
let fctn =
let a =123
let a =123
a
it doesn't produce any error.
I don't understand the difference. Can anyone please explain?
Edit : first code I write in module level.
回答1:
I agree this is confusing. The problem is that let
behaves differently when it is used as a local variable (within a function) and when it is used as a global definition (within a module).
Global definitions (in a module) are compiled as static members of a static class and so a name can be used only once. This means that top-level use of:
let a = 10
let a = 11
... is an error, because F# would have to produce two static members of the same name.
Local definitions (inside a function or other nested scope) is compiled to IL and the variable name essentially disappears (the IL uses stack instead). In this case, F# allows variable shadowing and you can hide variable of existing name. This can be inside a function, or even just a do
block:
do
let a = 10
let a = 11
()
This is a bit confusing, because variable shadowing only works inside local scopes but not at the top level. It makes sense when you know how things are compiled though..
回答2:
on scope and shadowing
as CaringDev mentioned (but not explained) you will probably see what the shadowing is about when you make the scope a bit more obvious (using the let ... in ...
construct #light
let you shorten a bit - but you still can use it even without #light off
)
Try this:
> let a = 233 in let a = 555 in a;;
val it : int = 555
as you can see the expression evaluates to the shadowed value of a
- but the original is not lost:
> let a = 233 in (let a = 555 in a), a;;
val it : int * int = (555, 233)
it's just not in scope in the inner let ... in ...
btw: you can rewrite your example to:
let fctn =
let a = 123 in
(let a =123 in a)
(I added the parentheses just to make this more obvious)
the other on the module level really defines a value for the scope of the module and is not really an expression but a definition
回答3:
The first defines two public values with the same name.
The second hides (shadows) a value.
With the first you would have externally visible change of state (a
behaves like mutable) whereas with the second you can't (you have two a
s in different scopes).
If you write your statements in #light off
ML syntax it becomes obvious immediately.
来源:https://stackoverflow.com/questions/36492223/error-fs0037-sometimes-very-confusing