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 (o开发者_如何学Cn 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.
精彩评论