F#具有名为"Type extension"的功能,使开发人员能够扩展现有类型。 有两种类型的扩展:内在扩展和可选扩展。第一个类似于C#中的部分类型,第二个类似于方法扩展(但更强大)。
要使用内部扩展,我们应该将两个声明放入同一个文件中。在这种情况下,编译器会将两个定义合并为一个最终类型(即这是一种类型的两个“部分”)。
问题是这两种类型对不同的成员和值有不同的访问规则:
// SampleType.fs
// "Main" declaration
type SampleType(a: int) =
let f1 = 42
let func() = 42
[<DefaultValue>]
val mutable f2: int
member private x.f3 = 42
static member private f4 = 42
member private this.someMethod() =
// "Main" declaration has access to all values (a, f1 and func())
// as well as to all members (f2, f3, f4)
printf "a: %d, f1: %d, f2: %d, f3: %d, f4: %d, func(): %d"
a f1 this.f2 this.f3 SampleType.f4 (func())
// "Partial" declaration
type SampleType with
member private this.anotherMethod() =
// But "partial" declaration has no access to values (a, f1 and func())
// and following two lines won't compile
//printf "a: %d" a
//printf "f1: %d" f1
//printf "func(): %d" (func())
// But has access to private members (f2, f3 and f4)
printf "f2: %d, f3: %d, f4: %d"
this.f2 this.f3 SampleType.f4
我阅读了F#规范但没有找到任何想法为什么F#编译器区分值和成员声明。
在V.1规范的8.6.1.3 section中,“实例定义定义的函数和值是词法范围(因此隐式私有)到被定义的对象。”部分声明可以访问所有私有成员(静态和实例)。我的猜测是,通过“词法范围”规范作者特别指的是“主要”声明,但这种行为对我来说似乎很奇怪。
问题是:这种行为是故意的,背后有什么理由?
答案 0 :(得分:10)
这是一个很好的问题!正如您所指出的那样,规范说“本地值词法范围到正在定义的对象”,但是看看F#规范,它实际上并没有定义词法范围在这种情况下的含义。
如您的示例所示,当前行为是对象定义的词法范围只是主要类型定义(不包括内部扩展)。我对此并不感到惊讶,但我发现其他解释也是有道理的......
我认为这样做的一个很好的理由是两种扩展应该表现得相同(尽可能多),并且您应该能够重构代码,使用其中一种来根据需要使用另一种扩展。这两种只是在封面下编译它们的方式不同。如果一种允许访问词法范围而另一种不允许访问词法范围,则该属性将被破坏(因为扩展成员在技术上不能这样做)。
那就是说,我认为这可以(至少)在规范中澄清。报告此问题的最佳方式是通过fsbugs
点microsoft
向com
发送电子邮件。
答案 1 :(得分:3)
我已将此问题发送至fsbugs
microsoft
点com
,并得到Don Syme的回复:
嗨谢尔盖,
是的,这种行为是故意的。在类范围中使用“let”时,标识符在类型定义上具有词法范围。该值甚至可能不会放在字段中 - 例如,如果某个值未被任何方法捕获,则它将成为构造函数的本地值。这种分析是在课堂上进行的。
据我所知,您希望该功能像C#中的部分类一样工作。然而,它只是不起作用。
我认为术语“词汇范围”应该在规范中更清晰地定义,因为否则当前的行为对其他开发者来说也会令人惊讶。
非常感谢Don的回应!