问题
In F# operator overloading seems powerful but also tricky to get right. I have following class:
type Value<'T> =
with
static member inline (+) (a : Value<'U>, b: Value<'U>) : Value<'U> =
do stuff
If i define another overload for + with :
static member inline (+) (a : Value<'U>, b: 'U) : Value<'U> =
do stuff
It works. But if i want a symmetric operator:
static member inline (+) (b: 'U, a : Value<'U>) : Value<'U> =
do stuff
The compiler complains:
let a = Value<int>(2);
let b = a + 3 // ok
let c = 3 + a //<-- error here
Error 3 Type inference problem too complicated (maximum iteration depth reached). Consider adding further type annotations
Is there a way around this and stay generic?
I am using F# 3.1
Thanks
回答1:
Removing the type annotations will solve the issue you pointed out, but you didn't notice there is another issue: try invoking the first overload, the compiler will not know which overload to call. It's a shame the overload resolution doesn't pick up the right one.
One tricky way to get everything working at compile time is to declare only the first overload into the type, and for the rest use the trick of redefining the (+)
operator using an intermediate type:
type Value<'T> = Value of 'T with
static member inline (+) (Value a, Value b) = Value (a + b)
type Sum = Sum with
static member inline (?<-) (Sum, a, b) = a + b
static member inline (?<-) (Sum, Value a, b) = Value (a + b)
static member inline (?<-) (Sum, b, Value a) = Value (a + b)
let inline (+) a b :'t = (?<-) Sum a b
// test
let v = Value(2)
let a = v + v
let b = v + 3
let c = 3 + v
let d = Value(Value 7) + Value(Value 10)
let e = 5 + 7
UPDATE
I've found another workaround which I prefer since it does not require redefining the (+) operator, the trick is to create a base class and move some overloads there:
type BaseValue<'T>(v : 'T) =
member x.V = v
type Value<'T>(v : 'T) =
inherit BaseValue<'T>(v : 'T)
static member inline (+) (a : Value<_>, b: Value<_>) = Value(b.V+a.V)
type BaseValue with
static member inline (+) (a: BaseValue<_>, b) = Value(b+a.V)
static member inline (+) (b, a: BaseValue<_>) = Value(b+a.V)
// test
let v = Value(2)
let a = v + v
let b = v + 3
let c = 3 + v
let d = Value(Value 7) + Value(Value 10)
let e = 5 + 7
回答2:
I think the problem lies in the fact that there is no way to resolve between the first and, say, the second overload when the second parameter to the second overload is a Value<'T>
itself.
Here is a complete version that causes the error:
type Value<'T>(v : 'T) =
member x.V = v
with
static member inline (+) (a : Value<'U>, b: Value<'U>) : Value<'U> =
Value<'U>(b.V+a.V)
static member inline (+) (a : Value<'U>, b: 'U) : Value<'U> =
Value<'U>(b+a.V)
static member inline (+) (b: 'U, a : Value<'U>) : Value<'U> =
Value<'U>(b+a.V)
let a = Value<int>(2);
let b = a + 3
let c = 3 + a
I'd say write one operator overload, and do pattern matching on type inside it? Ugly, I know.
来源:https://stackoverflow.com/questions/22401010/f-operator-overloading-riddle-2