错误FS0037有时候,非常混乱

Rav*_*are 3 f# functional-programming shadowing

如果我编写以下F#代码,编译器会发出错误.

let a = 123
let a = 123
Run Code Online (Sandbox Code Playgroud)

产生的错误是:

错误FS0037:值'a'的重复定义

如果我在这样的函数中编写相同的代码:

let fctn = 
    let a =123
    let a =123
    a
Run Code Online (Sandbox Code Playgroud)

它不会产生任何错误.

我不明白其中的区别.有人可以解释一下吗?

编辑:我在模块级写的第一个代码.

Car*_*ten 5

关于范围和阴影

正如CaringDev所提到的(但没有解释)你可能会看到当你使示波器更明显时阴影的含义(使用这个let ... in ...结构#light 你缩短一点 - 但你甚至可以使用它#light off)

试试这个:

> let a = 233 in let a = 555 in a;;
val it : int = 555
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,表达式计算为阴影a- 但原始值不会丢失:

> let a = 233 in (let a = 555 in a), a;;
val it : int * int = (555, 233)
Run Code Online (Sandbox Code Playgroud)

它只是在内心的范围内 let ... in ...

顺便说一句:你可以将你的例子重写为:

let fctn = 
    let a = 123 in
    (let a =123 in a)
Run Code Online (Sandbox Code Playgroud)

(我添加括号只是为了让这更明显)


模块级别的另一个实际上定义了模块范围的值,并不是一个表达式而是一个定义


Tom*_*cek 5

我同意这令人困惑。问题是let当它用作局部变量(在函数内)和用作全局定义(在模块内)时,行为不同。

全局定义(在模块中)被编译为静态类的静态成员,因此名称只能使用一次。这意味着顶级使用:

let a = 10
let a = 11
Run Code Online (Sandbox Code Playgroud)

... 是一个错误,因为 F# 必须生成两个同名的静态成员。

局部定义(在函数或其他嵌套范围内)被编译为 IL,变量名基本上消失了(IL 使用堆栈代替)。在这种情况下,F# 允许变量隐藏,您可以隐藏现有名称的变量。这可以在一个函数内,甚至只是一个do块:

do
  let a = 10
  let a = 11
  ()
Run Code Online (Sandbox Code Playgroud)

这有点令人困惑,因为变量阴影仅在局部作用域内起作用,而在顶层不起作用。当你知道事情是如何编译时,这是有道理的..