为什么F#编译器无法处理这个中缀运算符?

11

我有一个类和一个记录的定义如下:

namespace Foo
    type internal MyRecord = 
        {
            aValue1 : int
            aValue2 : int
        }
        static member (+) (left : MyRecord, right : MyRecord) : MyRecord =
            {aValue1 = left.aValue1 + right.aValue1; aValue2 = left.aValue2 + right.aValue2;}

    type internal Bar() =
        member this.Baz() = 
            let myRecord1 = {aValue1 = 2; aValue2 = 3;}
            let myRecord2 = {aValue1 = 7; aValue2 = 5;}
            let sum = myRecord1 + myRecord2 //Does not compile
            0

这个无法编译,报错信息如下:

成员或对象构造函数 'op_Addition' 不是公共的。私有成员只能从声明类型内部访问。受保护成员只能从扩展类型访问,不能从内部 lambda 表达式访问。

两种类型都是内部的。如果我明确将 + 运算符设置为 public,这也没有帮助:

static member public (+) (left : MyRecord, right : MyRecord) : MyRecord

有效的方法是放弃使用运算符并使用静态方法:

namespace Foo
    type internal MyRecord = 
        {
            aValue1 : int
            aValue2 : int
        }
        static member Add (left : MyRecord, right : MyRecord) : MyRecord =
            {aValue1 = left.aValue1 + right.aValue1; aValue2 = left.aValue2 + right.aValue2;}

    type internal Bar() =
        member this.Baz() = 
            let myRecord1 = {aValue1 = 2; aValue2 = 3;}
            let myRecord2 = {aValue1 = 7; aValue2 = 5;}
            let sum = MyRecord.Add(myRecord1, myRecord2) //Does compile
            0

当在这种情况下使用运算符时,为什么F#编译器会遇到麻烦,而使用命名成员却可以正常工作?

将两种类型都从internal更改为public也可以解决编译错误。

我正在使用Visual Studio 2012和F# 3.0,目标是.NET Framework 3.5。


1
这可能是编译器的一个错误 - 乍一看,您的代码似乎应该是有效的。您应该将此电子邮件发送到microsoft.com上的fsbugs,以便F#团队可以研究它。 - Jack P.
这对我来说看起来像是一个 Bug。似乎在解析op_Addition的约束时,该约束为AccessibleFromEverywhere而不是AccessibleFromSomewhere。实际修复似乎相当复杂,可能会影响编译器的其余部分。 - vcsjones
1个回答

6
我不知道为什么F#编译器会出现这个问题。这可能与F#处理运算符的方式有关,或者与可访问性的处理方式有关。您必须记住,在这种语言中,并非所有东西都是表面上看起来的那样。一些“面向对象”的特性是通过做出一些牺牲来实现的。也许这就是其中之一。
但是。我知道如何解决这个问题 :). 不要在实现文件中将类型设置为internal。而是使用签名。像这样定义文件Foo.fsi:
namespace Foo
    type internal MyRecord = 
        {
            aValue1 : int
            aValue2 : int
        }

    [<Class>]
    type Bar =
        member Baz : unit -> int

像这样的Foo.fs:

namespace Foo
    type MyRecord = 
        {
            aValue1 : int
            aValue2 : int
        }
        static member (+) (left : MyRecord, right : MyRecord) : MyRecord =
            {aValue1 = left.aValue1 + right.aValue1; aValue2 = left.aValue2 + right.aValue2;}

    type Bar() =
        member this.Baz() = 
            let myRecord1 = {aValue1 = 2; aValue2 = 3;}
            let myRecord2 = {aValue1 = 7; aValue2 = 5;}
            let sum = myRecord1 + myRecord2 //Compiles
            0

这将使您的代码有效且MyRecord内部。

使用签名文件是一个好的解决方案。它允许我保持我喜欢的语法。在JackP的评论建议下,我正在跟进,看看F#团队对此有何想法。 - vcsjones

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接