有什么方法可以修复或忽略 F# 中的警告 FS1240"此类型测试或向下转换将忽略度量单位"?



我有以下代码:

/// Constant for use in comparing floats within the ray tracer
[<Literal>]
let epsilon = 0.00001
/// Compares the two floats to see if they are epsilon away from each other.
/// See the definition of epsilon to see the resolution of the compare.
let compareFloat (x: float<'u>) (y: float<'u>) = abs(float x - float y) <= epsilon
/// Represents a 3D point
[<CustomEquality; NoComparison>]
type Point<[<Measure>] 'u> = { X: float<'u>; Y: float<'u>; Z: float<'u> } with
/// Overrides the Object.Equals method to provide a custom equality compare for Point records
override x.Equals object =
match object with
| :? Point<'u> as p -> compareFloat x.X p.X &&
compareFloat x.Y p.Y &&
compareFloat x.Z p.Z
| _ -> false
/// Overrides the Object.GetHashCode method, which is recommended when overriding Object.Equals
override x.GetHashCode () = hash x // Re-use the built-in hash for records

此代码是有效的代码,您可以简单地将其放入F#脚本文件中进行测试。代码段:? Point<'u>给出警告:

FS1240:此类型测试或下变频将忽略度量单位`u`

当然,实际上,我的Point类型还有几个成员,主要由算术运算符重载组成。然而,这就是问题所在。我最近扩展了该类型以能够处理单元,但我无法找到摆脱这种类型测试警告的方法。我之所以实现自定义相等,是因为我希望能够比较两个Point,主要是在测试中。我知道度量单位是编译时的事情,在运行时不可用,而且据我所知,类型测试:?发生在运行时。令人惊讶的是,使用:? Point<_>不起作用,如果忽略了度量单位,就会出现错误,这也没有多大意义,因为警告告诉我它无论如何都会被忽略。

  1. 有没有办法修改代码以消除此警告
  2. 如果没有,是否有更好的方法来实现自定义平等
  3. 这是个虫子吗

谢谢!

  1. 我不这么认为,但您可以使用#nowarn "1240"来抑制警告
  2. 不,我认为你所做的是合理的
  3. 不,我认为这只是F#中计量单位工作方式的一个缺陷。请参阅此reddit讨论

这个答案的灵感来自Fyodor的评论。

您正试图使用类型扩展来比较点,但度量单位在运行时不可用这一事实会导致问题。编译器会抱怨——尽管只有一个警告,所以你可以抑制它。但现在你的代码在检查相等性时似乎考虑了度量单位,这当然是不可能的。(我想知道这是否应该是一个错误,而不是警告(。

这里有一个替代解决方案,使用自定义运算符。

type Point<[<Measure>] 'u> = { X: float<'u>; Y: float<'u>; Z: float<'u> }
let epsilon = 0.00001

let compareFloat (x: float<'u>) (y: float<'u>) = abs(float x - float y) <= epsilon
let (==) p1 p2 =
// you could check for reference equality here first
compareFloat p1.X p2.X &&
compareFloat p1.Y p2.Y &&
compareFloat p1.Z p2.Z

和一些样本代码

[<Measure>]
type human
[<Measure>]
type klingon
let p1 = {X = 0.0<human>; Y = 1.0<human>; Z = 2.0<human> }
let p2 = {X = 0.0<human>; Y = 1.0<human>; Z = 2.000005<human> }
let p3 = {X = 1.0<human>; Y = 1.0<human>; Z = 2.5<human> }
let p4 = {X = 0.0<klingon>; Y = 1.0<klingon>; Z = 2.0<klingon> }
// won't compile
let p5 = {X = 0.0<human>; Y = 1.0<klingon>; Z = 2.0<klingon> }

// false, uses default equality
p1 = p2
// true
p1 == p2
// false
p2 == p3
// won't compile
// error FS0001: Type mismatch. Expecting a
//     'Point<human>'    
// but given a
//     'Point<klingon>'    
// The unit of measure 'human' does not match the unit of measure 'klingon'
p3 == p4

最新更新