无法在 F# 中重载布尔运算符

Posted

技术标签:

【中文标题】无法在 F# 中重载布尔运算符【英文标题】:Cannot overload boolean operators in F# 【发布时间】:2011-02-15 09:57:04 【问题描述】:

F# 确实允许重载像 + 这样的算术运算符,但似乎不允许重载像 || 这样的布尔运算符。以下代码生成一个警告和两个错误:

type MyBool =
    val Value : bool
    new(value) =  Value = value 
    static member (||) (v1: MyBool, v2 : MyBool) =
        new MyBool(v1.Value || v2.Value)
let b1 = new MyBool(true)
let b2 = new MyBool(false)
let b3 = b1 || b2

警告(关于静态成员 (||) 定义):名称“(||)”不应用作成员名称。如果定义静态成员以供其他 CLI 语言使用,请改用名称“op_BooleanOr”。

错误(在 'let b3' 语句中的 b1 和 b2 上):此表达式应为 bool 类型,但此处为 MyBool 类型

如果我使用 op_BooleanOr 而不是 (||) 警告会消失,但错误仍然存​​在。

当我对 MyInt 类型中的 + 运算符执行完全相同的操作时,不会出现警告或错误。那么,为什么当我尝试重载时会出现这些警告/错误 ||还是&&?

【问题讨论】:

【参考方案1】:

恐怕 F# 编译器没有对允许您覆盖它们的逻辑运算符进行任何处理(就像 C# 那样)。据我所知,x && y 被简单地编译为if x then y else false,所以x 必须是布尔值。我没有检查 F# 编译器是否支持 C# 中声明的类型的这种行为,但我认为不支持。

据我所知,为您自己的运算符模拟短路行为的最佳方法是使用 lazy 关键字来创建惰性值。然后你可以这样写:

let foo b = 
  printfn "foo %b" b
  MyBool(b)

lazy foo true &&! lazy foo false    // Calls 'foo' for both branches
lazy foo false &&! lazy foo false   // Calls 'foo' only for the first one

这两个运算符可以使用静态成员约束来定义,因此它们应该(原则上)适用于实现 C# 所需运算符的任何类型。

let inline (&&!) (x:Lazy<_>) (y:Lazy<_>) = 
  if (^T: (static member op_False : ^T -> bool) x.Value)
    then x.Value else x.Value &&& y.Value

let inline (||!) (x:Lazy<_>) (y:Lazy<_>) = 
  if (^T: (static member op_False : ^T -> bool) x.Value) 
    then x.Value else x.Value ||| y.Value

然后您可以使用所有必需的运算符定义您的 MyBool 类型(作为旁注,如果您这样定义它,它应该可以在 C# 中以自然方式使用):

type MyBool(b) =
  member x.Value = b
  static member (|||) (v1: MyBool, v2 : MyBool) = 
    MyBool(v1.Value || v2.Value) 
  static member (&&&) (v1: MyBool, v2 : MyBool) = 
    MyBool(v1.Value && v2.Value) 
  static member op_True (v: MyBool) = v.Value
  static member op_False (v: MyBool) = not v.Value

【讨论】:

感谢您的全面回答!【参考方案2】:

&amp;&amp;|| 与其他运算符的不同之处在于它们是短路的,因此不能简单地作为方法实现。由于这个 .net 定义了 special rules,您需要遵循以启用您自己的类型使用 &amp;&amp;||

简而言之:您还需要定义operator trueoperator false

【讨论】:

有没有办法在F#中实现operator trueoperator false?我找不到它。 @Halbe:其实我也不知道。对不起。 感谢您的回答。它澄清了无论如何警告和错误的原因!

以上是关于无法在 F# 中重载布尔运算符的主要内容,如果未能解决你的问题,请参考以下文章

在 sympy 中,如何将函数表达式转换为运算符重载表达式?

“运算符”的 C++ 模棱两可的重载

无法在监视窗口中使用重载运算符评估表达式

无法在 C++ 中重载流提取运算符 (>>)

F# 运算符重载用于转换多个不同的度量单位

如何使用布尔和字符串数据类型之间的比较运算符从数据框中过滤数据