在F#中键入扩展名和成员可见性

Ser*_*kov 17 f# lexical-scope type-extension

F#具有称为"类型扩展"的功能,使开发人员能够扩展现有类型.有两种类型的扩展:内在扩展可选扩展.第一个类似于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
Run Code Online (Sandbox Code Playgroud)

我读了F#规范但没有找到任何想法为什么F#编译器区分值和成员声明.

在F#规范的8.6.1.3 部分中,"实例定义定义的函数和值在词法范围内(因此隐式私有)到被定义的对象." 部分声明可以访问所有私有成员(静态和实例).我的猜测是,通过"词法范围"规范作者特别指的是"主要"声明,但这种行为对我来说似乎很奇怪.

问题是:这种行为是故意的,背后有什么理由?

Tom*_*cek 10

这是一个很好的问题!正如您所指出的,该规范说:"当地的数值词法范围的对象被定义",但看着F#的规格,它实际上并没有界定什么词汇范围在这种情况下指.

如您的示例所示,当前行为是对象定义的词法范围只是主要类型定义(不包括内部扩展).我对此并不感到惊讶,但我看到其他解释也有意义......

我认为一个好的原因是,两种类型的扩展应该表现相同(尽可能),你应该能够使用一个使用其他你需要重构你的代码.这两种只是在封面下编译它们的方式不同.如果一种允许访问词法范围而另一种不允许访问词法范围,则该属性将被破坏(因为,扩展成员在技术上不能这样做).

也就是说,我认为这可以(至少)在规范中澄清.报告最好的方法是发送电子邮件至fsbugsmicrosoftcom.

  • 我明白了两种类型扩展之间的相似性.但*intrinsic*和*optional*扩展的行为是不同的:可选扩展只能访问扩展类型的公共表面,但内部扩展仍然可以访问扩展类型的私有和受保护成员.所以他们也有不同的语义,不仅仅是他们如何在封面下编译. (2认同)