【问题标题】:F# and Operator Overloads: (>) and (^)F# 和运算符重载:(>) 和 (^)
【发布时间】:2025-12-23 18:35:06
【问题描述】:

好的,有人可以向我解释为什么 F# 允许您重载 > 和 ^ 运算符,但不允许您使用它们吗?

+ (op_Addition): Works just fine.
^ (op_Concatenate): Compiler error in F#. Apparently only strings can be concatenated.
> (op_GreaterThan): Runtime Error – Failure during generic comparison: the type Program+OppTest4 does not implement the System.IComparable interface.

如果我将我的 F# 代码编译为库并使用 VB 中的这些运算符,它们都可以工作。如果我使用 C# 中的这些运算符,除了 op_Concatenate 之外的所有运算符都可以工作(如预期的那样)。但是 F# 不仅忽略了其中一些,静态类型检查器甚至都懒得告诉你它打算这样做。

编辑代码示例

type OppTest4(value: int) =
   member this.value = value
   static member (^) (left : OppTest4, right : OppTest4) =
     OppTest4( Int32.Parse( left.value.ToString() ^ right.value.ToString()  ))
   static member (+) (left : OppTest4, right : OppTest4) =
     OppTest4(left.value + right.value )
   static member (>) (left : OppTest4, right : OppTest4) =
     left.value > right.value
   static member (<) (left : OppTest4, right : OppTest4) =
     left.value < right.value

【问题讨论】:

    标签: f# operator-overloading static-typing


    【解决方案1】:

    F# 对这些运算符符号具有默认含义,这对 F# 来说是合理的。您始终可以定义自己的含义来掩盖默认值,例如

    let (>) x y = ...
    

    例如,您可以将此运算符定义为“T.operator>(U)”(假设 x 的类型为 T,y 的类型为 U)。

    请参阅源代码分发中 FSharp.Core 中的 prim-types.fs 以了解默认定义。 (它们很重要!)

    鉴于 (1) 缺乏对 CLR 上的类型类机制的支持(用于在一组其他不相关的类型之间定义公共语义)和 (2) 原始类型(如 'int ') 通常需要对任何编程语言实现进行特殊处理(例如 System.Int32 没有定义 operator+ 方法,但大多数编程语言选择表现得好像存在这样的方法),很难想象任何一般可互操作的运算符的东西今天在 .Net 上的所有语言。有很多设计权衡取决于语言选择做什么(太多的交互问题在这里总结)。在任何情况下,您都应该能够从 F# 调用任何方法,并且如果不希望使用默认的运算符行为,您可以将运算符重新定义(阴影)为您想要的行为。如果您想到某个特定情况下无法工作,请告诉我。

    编辑

    我在

    添加了更多细节

    http://cs.hubfs.net/forums/thread/10869.aspx

    【讨论】:

    • 好的,那为什么这不起作用呢? “静态成员(>)(左:OppTest4,右:OppTest4)=left.value>right.value”
    • > 很难想象当今 .Net 上的所有语言之间有任何普遍可互操作的操作符。
    • 我很好奇我是否可以使用 let 绑定只为一种类型隐藏默认运算符定义 - 我无法控制类型实现。如果是这样,那将如何做?我试过 let (>) (a:SomeType) (b:SomeType) = ... 但是其他类型之间的任何比较都失败了,所以 2 > 1 不再编译了。
    • @emaster,不,您不能只为一种类型进行阴影。总之,如果我能在下周腾出几个小时,我会尝试在博客上写下这个,因为有很多不同的相互作用力量在起作用。
    • 与此同时,其他人在cs.hubfs.net/forums/thread/10860.aspx发布了一个很好的总结
    【解决方案2】:

    我同意,存在不一致:运算符可以定义,但不能使用。

    您是否在问,为什么 F# 设计者决定使用 System.IComparable 接口实现比较而不是运算符重载?我不知道为什么,但是在 OO 语言中,我更喜欢 IComparable 而不是运算符重载。因此,我建议 F# 开发人员打破 C# 兼容性并禁止“静态成员 (>) (...)” 语法糖。

    如果您要问如何调用这些重载运算符,这很容易:使用 op_Concatenate、op_GreaterThan 或 op_LessThan 静态成员。 (真的,我收到了一个编译器警告,描述了这个问题。F# 1.9.6.16)

    在没有任何编译器警告的情况下强制转换为 System.IComparable 的运行时错误绝对是一个错误。您可以将其发送至 fsbugs@microsoft.com。

    【讨论】:

    • 运行时错误不是错误,而是设计使然。然而,我还没有看到关于这种设计的公开讨论,即使不是很明显,它也很有动力,所以我会尽快纠正它。
    • 好吧,无法证明 OppTest4 的每个实例都没有 IComparable 接口。但是仍然知道有 OppTest4 实例不能转换为 IComparable !所以至少要有一个警告!为什么不呢?
    • @.vpolozov.name:看起来 F# 规范 (research.microsoft.com/en-us/um/cambridge/projects/fsharp/…) 的第 9.6 节表明他们正在考虑在使用比较运算符可能会失败的某些情况下添加警告。
    • @kvb 看来链接已失效 =(