简体   繁体   中英

Cannot overload boolean operators in F#

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 &&?

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

&& 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM