F# 中静态成员的不一致(显然)行为

Inconsistent (apparently) behaviour with static members in F#

以下前 2 个 F# 片段得到不同的结果,但我发现这有点不一致,尽管我知道成员和值具有不同的语义。我希望它们都得到相同的结果(第二个,因为它们具有相似的值语法)。

恕我直言,第三个片段明确定义了 get 方法,应该与第一个片段有所区别。

我一个人吗?

let mutable b = 0

type A = A with
    static member B = 
        b <- b + 1
        b

printfn "%d" A.B
printfn "%d" A.B
printfn "%d" A.B

//1
//2
//3
let mutable b = 0

type A = A

let _b = 
    b <- b + 1
    b

type A with
    static member B = _b

printfn "%d" A.B
printfn "%d" A.B
printfn "%d" A.B

//1
//1
//1
let mutable b = 0

type A = A with
    static member B 
        with get() = 
            b <- b + 1
            b

printfn "%d" A.B
printfn "%d" A.B
printfn "%d" A.B

//1
//2
//3

编辑

我同意 Tomas 的观点,语法有点误导。但是我可以看到困境在哪里:虽然值语法是 F#/functional,但成员符号必须复制 .NET/Object 定向行为。

对于那些感兴趣的,值行为静态成员可以是一个模式:

type MyType = MyType

let private myValueBehavingStaticPropertyValue = 
... potentially heavy workflow

type MyType with 
    static member MyValueBehavingStaticProperty = myValueBehavingStaticPropertyValue

有了这个,'potentially heavy workflow'就不会在我们每次读到属性

的时候重复了

The following first 2 F# snippets get different results, but I find this a little inconsistent, even though I understand that members and values have different semantics. I would expect both of them getting the same results (the second one, as they have a similar value syntax).

您期望计算 _b 会导致它重新计算自身,但这不是 F# 中值的计算方式。如果我定义 let x = 12 它不会在我每次访问它时重新评估 x 是什么。

这就是它总是打印 1 的原因。当 _b 第一次被访问时,它被评估一次,就是这样。

IMHO the third snippet, which explicitly defines a get method, should be differentiated from the first.

为什么?这与第一个片段没有什么不同。 F# 属性 定义如下:

type A =
    static member X = 12

使用 getter 编译,就像您明确定义它一样。

在此代码段中:

let mutable b = 0

type A = A

let _b = 
    b <- b + 1
    b

type A with
    static member B = _b

printfn "%d" A.B
printfn "%d" A.B
printfn "%d" A.B

_b 得到值 1,不管它后面的所有内容。 _b 永远不会重新评估。因此,虽然 属性 A.B 本身一直在重新评估,但它只会 return 已经设置好的值 _b

我觉得你的问题很好。 static 属性 的定义在语法上与值的定义非常相似 - 所以我明白为什么你认为两者的行为应该相同。

这有点误导,因为在 senes 的背后,属性有一个 getter 方法,每次访问 属性 时都会计算该方法。作为一个最小的例子,在下面,访问 A1.B 两次打印“hi”两次:

type A1 = A1 with
  static member B = 
    printfn "hi"

A1.B; A1.B

实际上在 F# 中有一个更详细的属性语法,它揭示了正在发生的事情——B 实际上是由编译代码调用的 get() 方法支持的事实幕后花絮:

type A2 = A2 with
  static member B 
    with get() = printfn "hi"

A2.B; A2.B

现在,您实际上永远不会这样做,但您甚至可以直接调用该方法(对 IntelliSense 隐藏):

A2.get_B(); A2.get_B()

您的第二个版本等同于第一个或第三个版本。这是:

let mutable b = 0

type A = A

let _b () = 
    b <- b + 1
    b

type A with
    static member B = _b ()

printfn "%d" A.B
printfn "%d" A.B
printfn "%d" A.B

//1
//2
//3

_b 在这里被标识为 function 而不是 value (如您的情况),因此被调用每次通过 A.B 调用静态成员时。所以我想说这种明显的不一致只有在混淆了 values(只计算一次)和 functions(它们是仅在每次调用它们时进行评估)。