错误FS0037有时候,非常混乱

时间:2016-04-08 05:32:05

标签: f# functional-programming shadowing

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

event_id

产生的错误是:

  

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

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

num_view_counts

它不会产生任何错误。

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

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

3 个答案:

答案 0 :(得分:5)

范围和阴影

正如CaringDev所提到的(但没有解释),当你使范围更明显时,你可能会看到阴影是什么(使用let ... in ...构造#light 你缩短了一点 - 但即使没有#light off

你仍然可以使用它

试试这个:

> let a = 233 in let a = 555 in a;;
val it : int = 555

正如您所见,表达式的计算结果为a阴影值 - 但原文不会丢失:

> let a = 233 in (let a = 555 in a), a;;
val it : int * int = (555, 233)

它不在内部let ... in ...

的范围内

btw:你可以将你的例子重写为:

let fctn = 
    let a = 123 in
    (let a =123 in a)

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

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

答案 1 :(得分:2)

第一个定义了两个具有相同名称的公共值。

第二个隐藏(阴影)一个值。

第一个你会有外部可见的状态变化(a表现得像是可变的)而第二个你不能(你在不同的范围内有两个a)。 / p>

如果您使用#light off ML语法编写语句,则会立即显现。

答案 2 :(得分:2)

我同意这令人困惑。问题是let在用作局部变量(在函数内)以及用作全局定义(在模块中)时表现不同。

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

let a = 10
let a = 11

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

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

do
  let a = 10
  let a = 11
  ()

这有点令人困惑,因为变量阴影仅适用于本地范围,但不适用于顶级。当你知道如何编译事物时,它是有道理的。