为什么 F# 构造函数中的"let"创建私有成员而不是局部变量



我正在学习一些F#,并试图弄清楚构造函数是如何工作的。

我想写一个类,它接受一些输入数据,对其进行解析,并通过成员变量将结果提供给外部世界。解析过程很简单,所以我想在这个过程中创建一些局部变量,但如果它们不成为私有成员变量,我该怎么做呢?AFAICT,使用let创建私有成员变量与使用member private几乎相同,但我不希望这些临时变量污染对象的命名空间。

这是迄今为止我能想到的最好的:

type MyClass( inputData ) = 
let _parsedData = 
// simulate expensive parsing of the input data
let work = sprintf "< %s >" inputData
sprintf "[%s]" work
member this.parsedData = _parsedData
member this.dump () = 
// this doesn't compile (as expected)
//printfn "work = %s" work
// I want this to *not* compile (because _parsedData is a local variable in the constructor)
printfn "_parsedData = %s" _parsedData
[<EntryPoint>]
let main argv = 
let obj = MyClass "hello, world!"
printfn "obj.parsedData = %s" obj.parsedData
obj.dump()
0 

但是_parsedData变成了一个私有成员变量,这是不必要的,因为它只是一个临时工作变量,并且最终值存储在this.parsedData中。我链接到上面的SO帖子表明,使用let创建的变量将充当局部变量并被丢弃,只要它们在其他成员中没有被引用,但定义this.parsedData以返回_parsedData的行为足以保持_parsedData的有效性。

我可以使用懒惰评估:

let _parsedDataLazy = lazy ( 
// simulate expensive parsing of the input data
let work = sprintf "< %s >" inputData
sprintf "[%s]" work
) 
member this.parsedDataLazy = _parsedDataLazy.Value

但这并没有真正的帮助,因为它仍然存在_parsedDataLazy成为私有成员变量的问题(尽管在这种情况下,这是有意义的(。这种方法还意味着保持inputData活动直到第一次调用parsedDataLazy,这可能是不希望的/不可能的。

我还想过使用val来定义成员变量,然后执行解析代码来填充它,但do绑定必须出现在任何member的:-/之前

我只想能够在构造函数中使用局部变量,计算一个值,然后将其存储在对象中。既然已经有了一种方法,let为什么要创建一个私有成员变量?!构造函数的目的是初始化正在创建的对象,它只是一个函数,所以我不明白为什么对何时可以执行代码或不同的行为有这些特殊的限制(例如,如果我使用let在成员函数中定义一个新变量,它不会作为成员变量被提升到对象中(。

顺便说一句,如果我使用let在构造函数中创建一个私有成员变量,如下所示:

let _foo = 42

然后我这样访问它:

let member this.printFoo () =
printfn "_foo = %s" _foo // no "this"

但如果我这样创建它:

member private _foo = 42

然后我这样访问它:

let member this.printFoo () =
printfn "_foo = %s" this._foo // uses "this"

这种不同的语法表明,前者是在构造函数上创建一个闭包,并在对象的生命周期内保持_foo变量的有效性,而不是_foo实际上是对象的成员。这就是实际发生的事情吗?

回答@konst sh的问题"为什么你认为应该在每次通话中对其进行评估">,我认为不应该,但我不是这么认为的。

我的理解是,对于下面的代码,组成parsedData的3条语句是一个表达式,其计算结果为字符串(sprintf的输出(,该字符串存储在成员变量中。

type MyClass( inputData ) =
member this.parsedData =
// simulate expensive parsing of the input data
printfn "PARSE"
let work = sprintf "< %s >" inputData
sprintf "[%s]" work
[<EntryPoint>]
let main argv =
let obj = MyClass "hello, world!"
printfn "CONSTRUCTED"
printfn "obj.parsedData = %s" obj.parsedData
printfn "obj.parsedData = %s" obj.parsedData
0

但当我运行它时,我会得到这个:

CONSTRUCTED
PARSE
obj.parsedData = [< hello, world! >]
PARSE
obj.parsedData = [< hello, world! >]

我希望看到这个:

PARSE
CONSTRUCTED
obj.parsedData = [< hello, world! >]
obj.parsedData = [< hello, world! >]

在VSCode中逐步执行还可以确认这3条语句执行了两次。但是parseData不是函数,对吧?为此,我需要这样定义它:

member this.parsedData () =
...

感觉我错过了一些基本的东西…:-(

最新更新