【问题标题】:Why does the F# compiler fail with this infix operator?为什么 F# 编译器会因此中缀运算符而失败?
【发布时间】:2013-07-04 17:26:16
【问题描述】:

我有一个类和一个这样定义的记录:

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 表达式访问。

这两种类型都是内部的。如果我将+ 运算符明确设置为公开,那也无济于事:

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# 编译器在使用命名成员工作得很好的情况下难以使用运算符?

将这两种类型更改为 public 而不是 internal 也可以解决编译错误。

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

【问题讨论】:

  • 这可能是一个编译器错误——乍一看,您的代码看起来应该是有效的。您应该通过电子邮件将其发送至fsbugsmicrosoft.com,以便 F# 团队可以对其进行调查。
  • 看起来对我来说就像一个错误。似乎在解决op_Addition 的约束时,约束是AccessibleFromEverywhere 而不是AccessibleFromSomewhere。实际的修复看起来相当复杂,但不会影响编译器的其余部分。

标签: f#


【解决方案1】:

我不知道为什么 F# 编译器会出现这个问题。这可能与 F# 中处理运算符的方式或处理可访问性的方式有关。您必须记住,在这种语言中,并非一切都是它看起来的样子。一些“面向对象”的特性是通过做出一些牺牲来实现的。也许这就是其中之一。

但是。我知道如何解决这个问题:)。不要在实现文件中使您的类型成为内部类型。而是使用签名。像这样定义文件 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# 团队是否对此有任何想法。
猜你喜欢
  • 1970-01-01
  • 2014-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-01
  • 2013-04-08
相关资源
最近更新 更多