具有度量单位的 F# 类型与 System.Math

F# types with units of measure vs System.Math

我定义了自己的度量单位来表示弧度:

[<Measure>]
type rad

然后我意识到有一个值,例如类型 float<rad> 我不能使用 System.Math 中定义的许多函数(因为它们使用 'plain' 浮点数)

let valueWithUnit = 5.45<rad>
let absValue = Math.Abs valueWithUnit // <--- error!!

我创建了一段简单的代码,允许函数也处理 float<rad> 值:

let liftToRadFunc (f : float -> float) (arg : float<rad>) =
    arg * 1.</rad>
    |> f
    |> (*) 1.<rad>

let result = (liftToRadFunc Math.Abs) valueWithUnit // <--- now works fine

但它的问题是它完全不通用。假设我也想引入一个单位来表示度数——然后呢?我是否需要复制代码并将所有单位从 "rad" 更改为 "deg" ?或者也许有更好的解决方案?

F# 的 core operators 主要是度量单位感知的。例如,abs 使用度量单位并呈现 System.Math.Abs 冗余。

可以根据使用的单位从问题中概括函数:

let liftToKeepUnits f (arg : float<'u>) : float<'u> =
    float arg |> f |> LanguagePrimitives.FloatWithMeasure

虽然这只是保留单位,但只有在函数对单位没有影响的情况下才是正确的。所以我会使用这个提升函数来创建特定的、单位感知的函数,而不是在使用它们的地方提升函数。

根据我的经验,最有用的工具是根据度量单位使类型和函数通用的能力。类型推断本身并不总是这样做,但添加度量单位类型注释通常可以解决问题。在更复杂的情况下,LanguagePrimitives.FloatWithMeasureLanguagePrimitives.Float32WithMeasure 允许与类型注释一起将单位添加到禁止使用通用单位的非零值的约束中,否则 运行 -测量参数。

另外,类型可以有这样的度量单位参数:

type MyType<[<Measure>] 'u> ...

当使用所有这些功能时,不知道单位的功能成为例外,在其余情况下手动添加或删除单位不是什么大问题。