F#运算符重载谜语

F#运算符重载谜语,f#,operator-overloading,F#,Operator Overloading,在F#中,运算符重载似乎很强大,但要正确处理也很棘手。 我有以下课程: type Value<'T> = with static member inline (+) (a : Value<'U>, b: Value<'U>) : Value<'U> = do stuff 类型值,b:值= 做事 如果我为+定义另一个重载,则使用: static member inline (+) (a :

在F#中,运算符重载似乎很强大,但要正确处理也很棘手。 我有以下课程:

 type Value<'T> = 
    with
        static member inline  (+) (a : Value<'U>, b: Value<'U>) : Value<'U> = 
           do stuff
类型值,b:值=
做事
如果我为+定义另一个重载,则使用:

static member inline  (+) (a : Value<'U>, b: 'U) : Value<'U> = 
           do stuff
静态成员内联(+)(a:值=
做事
编译器抱怨:

let a = Value<int>(2);
let b = a + 3 // ok
let c = 3 + a //<-- error here
设a=值(2);
设b=a+3//ok

让c=3+a/编译器永远别无选择:当你应用
(+)
操作符时,你要么给它int类型的东西,要么给它Value类型的东西,反之亦然

让我们在解释器中试试这个。我将两个实现输出A和B,这样我们就可以知道调用的是哪一个:

> type Value<'T> = 
-       { a : 'T  }
-     with
-         static member inline (+) (a : Value<'U>, b: Value<'U>) : Value<'U> = 
-            printfn "A"; a
-         static member inline (+) (a : Value<'U>, b: int) : Value<'U> = 
-            printfn "B"; a
- ;;

type Value<'T> =
  {a: 'T;}
  with
    static member ( + ) : a:Value<'U> * b:Value<'U> -> Value<'U>
    static member ( + ) : a:Value<'U> * b:int -> Value<'U>
  end
好的。现在

>t+t;;
A.
val it:Value={a=“foo”;}
如中所述,我的首选解决方案是对某些重载使用基类:

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

type BaseValue请注意,如果在成员上使用与在类本身上相同的类型参数,则此问题将消失:

type Value<'t when 't : (static member (+) : 't * 't -> 't)> = V of 't with
    static member inline (+)(V(x:'t), V(y:'t)) : Value<'t> = V(x+y)
    static member inline (+)(V(x:'t), y:'t)    : Value<'t> = V(x+y)
    static member inline (+)(x:'t,    V(y:'t)) : Value<'t> = V(x+y)
类型值't)>=t的V
静态成员内联(+)(V(x:'t),V(y:'t)):值=V(x+y)

静态成员内联(+)(x:'t,V(y:'t)):Value此运算符定义不合理,但有趣的是,编译器的反应也不合理

首先,考虑添加两个
Value
对象的预期返回类型。它可能是
!撇开语言语义不谈,没有理由假设像这样的完全泛型类型不应该嵌套在其自身中
+
运算符定义不正确。
类型推断总是会在添加两个
实例的重载上失败,因为编译器将无法解决歧义

但更根本的问题是:这个操作符到底是什么意思?添加值,并可能将它们包装在一个额外的类型中?这是两个完全不同的行动;我看不出把它们结合在一起有什么好处,更不用说含蓄和可能模棱两可的方式了。只为两个
Value
实例定义加法并在需要的地方显式构造它们,可能会节省很多麻烦


除此之外,在这种情况下,编译器看起来相当不直观。详细了解F#规范的人可能能够判断这是错误还是不一致的设计,但看看类似变体的行为:

type Value<'T> =
    { Raw : 'T }

    static member inline (+) (v, w) = { Raw = v.Raw + w.Raw }
    static member inline (+) (v, a) = { Raw = v.Raw + a }
    static member inline (+) (a, v) = { Raw = a + v.Raw }

let a = { Raw = 2 }
let b = a + 3           // ok
let c = 3 + a           // ok
let d = { Raw = obj() } // No problemo

type value实际上我将重新表述这个问题,因为我问错了。非常感谢您的回答,对于记录类型,它甚至可以在没有注释的情况下工作–并且允许使用诸如
obj
之类的类型进行实例化。看起来有些不一致的编译器行为.Epic。此代码将IntelliSense锁定在VS2015中相当长一段时间。我只需添加一些值,然后编辑定义了
v
的行,就把它写了大约两分钟。@vandroy我并不感到惊讶,很可能与此有关
> t + t;;
A
val it : Value<string> = {a = "foo";}
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
type Value<'t when 't : (static member (+) : 't * 't -> 't)> = V of 't with
    static member inline (+)(V(x:'t), V(y:'t)) : Value<'t> = V(x+y)
    static member inline (+)(V(x:'t), y:'t)    : Value<'t> = V(x+y)
    static member inline (+)(x:'t,    V(y:'t)) : Value<'t> = V(x+y)
type Value<'T> =
    { Raw : 'T }

    static member inline (+) (v, w) = { Raw = v.Raw + w.Raw }
    static member inline (+) (v, a) = { Raw = v.Raw + a }
    static member inline (+) (a, v) = { Raw = a + v.Raw }

let a = { Raw = 2 }
let b = a + 3           // ok
let c = 3 + a           // ok
let d = { Raw = obj() } // No problemo
type Value<'T> =
    val Raw : 'T
    new (raw) = { Raw = raw }
    static member inline (+) (v : Value<_>, w : Value<_>) = Value(v.Raw + w.Raw)
    static member inline (+) (v : Value<_>, a)            = Value(v.Raw + a)
    static member inline (+) (a, v : Value<_>)            = Value(a + v.Raw)

let a = Value(2)
let b = a + 3      // OK
let c = 3 + a      // OK
let d = Value(obj) // No problemo