F#: mutable 关键字不适宜用于结构中声明可变值

 关于之前日志F#编译器的一个Bug》,有一个问题,Set函数既然运行正常,改变的值去哪里了?以下例子做出了解答:
 
[<Struct>]
type TestStruct =

    val mutable X: int

    new(x) = {_x = x}

    member this.Set(x) =

        this._x <- x

        this
 

let a = TestStruct(2)

let c() =

    let b = a.Set 10

    b.X

c();;

 
代码运行结果为10(我们想要的结果)。众所周知,类的子函数,其背后的实现是一个带对象输入的公共函数,调用a.Set(x),等效于执行伪代码:

Set(a, x) 。

由此可见,当我们绑定 
let a = TestStruct(2),调用a.Set(x) 的时候,传递到Set函数中的this,其实是个复制值。
而当
我们绑定 let mutable a = TestStruct(2),调用a.Set(x) 的时候,传递到Set函数中的this,却是a本身。

 
虽然,当我们在结构中使用
mutable 的时候,可以使用返回this的方式,返回一个新的结构值。这也很符合函数编程的规范。但是如果可变数值使
用的是引用单元格,而不是
mutable 的话。表现又略微不同:

 
mutable 方式,a值和Set返回值不同,一个表示原始值,另一个表示修改后的值;

引用单元格方式,a就是修改后的值。

[<Struct>]

type TestStruct =

    val _x: int ref

    new(x) = {_x = ref x}

    member this.Set(x) =

        this._x := x

member this.X = !this._x; 


let a = TestStruct(2)

let c() =

    a.Set 10

    a.X

 

c();;


以上这段代码,可以得到正确值10,原理很简单,int ref实际上是一个类,所以结构复制后,其对应的对象都是同一个。 
 

所以用户仍然无法从习惯风格本身理解 
TestStruct.Set(x)函数的行为。此外.Net库里面常用的一个StringBiulder类,它的行为特征,则完全符合引用单元格方式。所以结构中,mutable 的使用要尤其审慎。毕竟,一个结构,如果let绑定使用和let mutable绑定使用,两者之间行为不一致的话,利用这种特性写出的代码,未免太奇淫技巧一点,徒增读代码的难度。


原文地址:https://www.cnblogs.com/greatim/p/3917991.html