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

发布于 2024-10-17 14:14:48 字数 617 浏览 4 评论 0原文

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 类型中的 + 运算符执行完全相同的操作时,没有警告或错误。那么,为什么当我尝试超载时会出现这些警告/错误 ||或&&?

F# does allow overloading of arithmetic operators like +, but seems to disallow this for boolean operators like ||. The following code generates a warning and two errors:

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

Warning (on the static member (||) definition): The name '(||)' should not be used as a member name. If defining a static member for use from other CLI languages then use the name 'op_BooleanOr' instead.

Error (on b1 and b2 in the 'let b3' statement): This expression was expected to have type bool but here has type MyBool

If I use op_BooleanOr instead of (||) the warning disappears, but the errors remain.

When I do exactly the same thing for the + operator in a MyInt type there are no warnings or errors. So, why do these warnings/errors appear when I try to overload || or &&?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

活雷疯 2024-10-24 14:14:48

恐怕 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

I'm afraid that the F# compiler doesn't have any treatment of logical operators that would allow you to override them (as C# does). As far as I can tell, x && y is compiled simply as if x then y else false, so x has to be boolean. I didn't check whether the F# compiler supports this behavior for types declared in C#, but I don't think it does.

As far as I know, the best way to emulate short-circuiting behvaior for your own operator is to use the lazy keyword to create lazy values. Then you can write something like:

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

The two operators can be defined using static member constraints, so they should (in principle) work for any types that implement the operators required by 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

Then you can define your MyBool types with all the required operators (as a side note, it should be usable in the natural way from C# if you define it like this):

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
谎言月老 2024-10-24 14:14:48

&&|| 与其他运算符的不同之处在于它们是短路的,因此不能简单地实现为方法。因此 .net 定义了特殊规则 您需要按照以下步骤启用 &&|| 与您自己的类型的使用。

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

&& and || differ from other operators in that they're short-circuiting and can thus not be implemented simply as methods. Because of this .net defines special rules you need to follow to enable the usage of && and || with your own types.

In short: you need to define operator true and operator false as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文