【问题标题】:Type extensions and members visiblity in F#F# 中的类型扩展和成员可见性
【发布时间】:2013-03-27 14:36:51
【问题描述】:

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# 编译器区分值和成员声明。

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

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

【问题讨论】:

    标签: f# lexical-scope type-extension


    【解决方案1】:

    我将此问题发送至fsbugsmicrosoft dot com,并得到了 Don Syme 的以下答复:

    嗨,谢尔盖,

    是的,这种行为是故意的。当您在类范围中使用“let”时,标识符在类型定义上具有词法范围。该值甚至可能不会放在一个字段中——例如,如果一个值没有被任何方法捕获,那么它就成为构造函数的本地值。这种分析是在类本地完成的。

    我了解您希望该功能能够像 C# 中的部分类一样工作。然而,它只是不那样工作。

    我认为术语“词法范围”应该在规范中更清楚地定义,否则当前的行为也会令其他开发人员感到惊讶。

    非常感谢唐的回复!

    【讨论】:

      【解决方案2】:

      这是一个很好的问题!正如您所指出的,规范说“局部值是词法作用域到被定义的对象”,但是查看 F# 规范,它实际上并没有定义在这种情况下词法作用域的含义。

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

      我认为这样做的一个很好的理由是这两种扩展应该表现相同(尽可能),并且您应该能够根据需要将代码从使用一种重构为使用另一种。这两种类型的区别仅在于它们在封面下的编译方式。如果一种允许访问词法范围而另一种不允许访问,则此属性将被破坏(因为,扩展成员在技术上无法做到这一点)。

      也就是说,我认为这可以(至少)在规范中得到澄清。报告此问题的最佳方法是发送电子邮件至fsbugsmicrosoftdotcom

      【讨论】:

      • 我明白你关于两种类型扩展之间的相似性的观点。但是 intrinsicoptional 扩展的行为是不同的:可选扩展只能访问扩展类型的公共表面,但内部扩展仍然可以访问扩展类型的私有和受保护成员.所以他们也有不同的语义,不仅仅是他们如何在封面下编译。
      • @SergeyTeplyakov 嗯,这是一个很好的观点。你说得对,他们在这方面有所不同。我想关键点是在这种情况下“词法范围”的定义(F# 规范中显然没有)。
      • 谢谢,托马斯。我认为您对词法范围是正确的。我的观点是,C# 中的部分类型是设计人员支持的众所周知且广泛使用的功能。但是由于这种限制,我们不能将内在扩展视为功能齐全的部分类型声明。这意味着在内部扩展受到此类限制之前,我们无法获得任何设计人员支持(针对 WinForms 和 WPF)。
      猜你喜欢
      • 1970-01-01
      • 2013-08-04
      • 1970-01-01
      • 2013-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-19
      • 1970-01-01
      相关资源
      最近更新 更多