在F#中不可变类型的含义

8
我知道F#中的变量默认是不可变的。 但是,在F#交互中,例如:
  > let x = 4;;

val x : int = 4

> let x = 5;;

val x : int = 5

> x;;
val it : int = 5
> 

所以,我将4分配给x,然后将5分配给x,它就会改变。这是正确的吗?应该会产生一些错误或警告吗?还是我只是不理解它是如何工作的?


1
可能是 https://dev59.com/8HE85IYBdhLWcg3wUBkZ 的重复问题。那里的答案解释了变量绑定和变量赋值之间的区别。 - Heatsink
1
@Alex:你可能会发现这个问题的答案很有帮助。 - Daniel
这个问题很相似,但不是重复的。 - Onorio Catenacci
4个回答

15
当你写下 let x = 3 时,你是将标识符 x 绑定到值 3。如果在同一作用域中再次这样做,你就声明了一个新的标识符,它隐藏了之前的标识符,因为它们具有相同的名称。
在 F# 中,通过使用破坏性更新运算符 <- 来改变一个值。对于不可变的值,这会失败,例如:
> let x = 3;;

val x : int = 3

> x <- 5;;

  x <- 5;;
  ^^^^^^

stdin(2,1): error FS0027: This value is not mutable
为了声明一个可变变量,在let之后添加mutable:
let mutable x = 5;;

val mutable x : int = 5

> x <- 6;;
val it : unit = ()
> x;;
val it : int = 6

但是你可能会问,这两者之间有什么区别?举个例子可能足够了:

let i = 0;
while i < 10 do
    let i = i + 1
    ()

尽管看起来不像,但这是一个无限循环。在循环内部声明的变量i与外部的i不同,它隐藏了外部的i。外部的i是不可变的,因此它始终保持其值为0,导致循环永远不会结束。正确的写法是使用一个可变变量:

let mutable i = 0;
while i < 10 do
    i <- i + 1
    ()

5

x并没有被改变,只是被下一条声明隐藏了。例如:

> let x = 4;;
val x : int = 4
> let x = "abc";;
val x : string = "abc"
>

隐藏和不变之间有什么区别?这是否意味着我仍然可以获取 x 的先前值? - Alexan
1
@Alex,请看我的回答,了解其中的区别。 - Asik

4
你没有将5分配给x,而是定义了一个新的变量。
以下示例显示有两个不同的变量。 (它还显示如果它在闭包中,被另一个函数使用,则可以“访问”旧的x):
let x = 5;;
let f y = y+x;;
f 10;;
let x = 0;;
f 10;;

产量
>
val x : int = 5

>
val f : int -> int
> val it : int = 15
>
val x : int = 0

> val it : int = 15

正如您所见,对f的两次调用都使用了第一个变量x。定义let x = 0;;定义了一个新变量x,但并没有重新定义f。


3
以下是一个简单的示例,说明 F# 中标识符“隐藏”的概念:
```fsharp let x = 1 let f () = let x = 2 // 隐藏外部定义的 x printfn "%d" x f () printfn "%d" x // 外部定义的 x 仍然可见 ```
在上述代码中,函数 `f` 中的 `x` 隐藏了外部定义的 `x`。在函数内部,`x` 的值为 2,在函数外部,`x` 的值仍为 1。
let x = 0
do //introduce a new lexical scope
    let x = 1 //"shadow" (i.e. hide) the previous definition of x
    printfn "%i" x //prints 1
//return to outer lexical scope
printfn "%i" x //prints 0, proving that our outer definition of x was not mutated by our inner definition of x

您的示例实际上有些复杂,因为您正在使用F# Interactive (FSI)。 FSI会动态生成类似于您示例中以下代码的代码:

module FSI_0001 =
    let x = 4;;

open FSI_0001 //x = 4 is now available in the top level scope
module FSI_0002 =
    let x = 5;;

open FSI_0002 //x = 5 is now available in the top level scope, hiding x = 4
module FSI_0003 =
    let it = x;;

open FSI_0003
//... subsequent interactions

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接