TL; DR 如何编写一个“重载”函数,该函数可以处理单位和具有相同单位的单独类型的所有数值类型({{1} },float32<m> -> Vector2<m>
)?
我有一个很好的小Vector2类,但是我想允许将度量单位附加到它上面,所以我基本上就这样定义了它(但是功能比这里要多得多):
int<kg> -> Vector2<kg>
我已经能够毫不费力地编写重载算术运算符(例如type Vector2<[<Measure>] 'u>(x: float32<'u>, y: float32<'u>) =
member this.X = x
member this.Y = y
),但我一直在努力编写一个可以工作的构造函数运算符(称为static member (+) ...
)包含所有数字类型和单位(允许我写@@
而不是1<m> @@ 2.5<m>
。在我将单位附加到此课程之前,这很简单:
new Vector2<m>(1.<m>, 2.<m>)
到目前为止,我已经编写了一个帮助类来处理这个问题:
let inline (@@) x y = new Vector2(float32 x, float32 y)
但似乎无法编写适当的类型约束来处理此调用案例。例如,像这样的东西不起作用
type OverloadedOperators =
static member CreateVector2 (x: float32<'u>, y: float32<'u>) = new Vector2<'u>(x, y)
static member CreateVector2 (x: float<'u>, y: float<'u>) = new Vector2<'u>(x |> float32 |> Float32WithMeasure<'u>, y |> float32 |> Float32WithMeasure<'u>)
static member CreateVector2 (x: int<'u>, y: int<'u>) = new Vector2<'u>(x |> float32 |> Float32WithMeasure<'u>, y |> float32 |> Float32WithMeasure<'u>)
因为let inline (@@) (x: 'u when 'u : (static member CreateVector2 : 'u * 'u -> Vector2<'v>)) y = OverloadedOperators.CreateVector2 (x, y)
,The type 'u is not compatible with float32<'v>
等。
我不知道怎么写这最后一小块。我想要的甚至可能吗?或者我应该忍受不断在任何地方使用The type 'u is not compatible with int<'v>
构造函数?
编辑感谢@JohnPalmer,我已经非常接近了,基于他的答案,我已经提出了最后一点:Vector2
。现在最后一步是“取消元组”这个函数,让它成为一个中缀运算符,但这似乎是不可能的,因为它甚至不知道它总是一个元组。我想我可能已经尽可能地用F#来到这里,但如果我错了,请纠正我!与此同时,我将使用let inline vec2 p = convert *** p
,这比 lot 更好,而不是每次调用构造函数时都必须内联所有杂乱的单元类型转换。
答案 0 :(得分:1)
这是一个有点hacky的解决方案,它是根据我们在这种情况下使用的标准技巧构建的:
open LanguagePrimitives
type Vector2<[<Measure>] 'u>(x: float32<'u>, y: float32<'u>) =
member this.X = x
member this.Y = y
type OverloadedOperators() =
static member CreateVector2 (x: float32<'u>, y: float32<'u>) = new Vector2<'u>(x, y)
static member CreateVector2 (x: float<'u>, y: float<'u>) = new Vector2<'u>(x |> float32 |> Float32WithMeasure<'u>, y |> float32 |> Float32WithMeasure<'u>)
static member CreateVector2 (x: int<'u>, y: int<'u>) = new Vector2<'u>(x |> float32 |> Float32WithMeasure<'u>, y |> float32 |> Float32WithMeasure<'u>)
static member ( *** ) (T,(x:float32<'u>,y)) = OverloadedOperators.CreateVector2(x,y)
static member ( *** ) (T,(x:float<'u>,y)) = OverloadedOperators.CreateVector2(x,y)
static member ( *** ) (T,(x:int<'u>,y)) = OverloadedOperators.CreateVector2(x,y)
let convert =OverloadedOperators()
(* testing *)
[<Measure>]
type m
convert *** (1<m>,1<m>)
convert *** (1.0<m>,1.0<m>)
它使用两个运算符并且过于冗长,但与您想要的相当接近
答案 1 :(得分:0)
let inline (@@) (x: float32<'u>) (y: float32<'u>) = new Vector2<'u>(x, y)
怎么样?没有这个诀窍?它至少对我有用。
答案 2 :(得分:0)
从@ JohnPalmer的回答开始,我能够走到最后一英里;只是对我没想到的类型签名的简单更改。这是第一部分:
type OverloadedOperators() =
static member CreateVector2 (_, x: float32<'u>, y: float32<'u>) = new Vector2<'u>(x, y)
static member CreateVector2 (_, x: float<'u>, y: float<'u>) = new Vector2<'u>(x |> float32 |> Float32WithMeasure<'u>, y |> float32 |> Float32WithMeasure<'u>)
static member CreateVector2 (_, x: int<'u>, y: int<'u>) = new Vector2<'u>(x |> float32 |> Float32WithMeasure<'u>, y |> float32 |> Float32WithMeasure<'u>)
let ops = new OverloadedOperators()
给运营商签名:
let inline (@@) (x: ^a) (y: ^b) = ((^T or ^a or ^b) : (static member CreateVector2 : ^T * ^a * ^b -> Vector2<'u>) (ops, x, y))
所以我们拥有它:成功的运算符重载适用于任何数字类型和度量单位。