F#'unbox float x'与'unbox int x'奇怪的编译结果

时间:2014-02-15 13:06:13

标签: f# type-inference units-of-measurement

在我查看以下问题时出现了这个问题:F# Unit of Measure, Casting without losing the measure type 请注意,我并没有尝试使用此unbox代码,我在回答问题时发现了一些奇怪的行为。

为什么以下代码可以正常工作

let intToFloat (x:int<'u>) : float<'u> = unbox float x
intToFloat 1<second>

虽然这会产生 System.InvalidCastException:无法将类型为'float32ToFloat @ 86-6'的对象强制转换为'Microsoft.FSharp.Core.FSharpFunc`2 [System.Single,System.Double]'。

let float32ToFloat (x:float32<'u>) : float<'u> = unbox float x
float32ToFloat 1.0f<second>

如果我在(float x)周围放置了代码,代码按预期工作,那么我认为它必须是一些表达式评估/类型推断规则。这里究竟发生了什么,为什么第二种情况下需要这些鹦鹉?

1 个答案:

答案 0 :(得分:3)

代码段中的细微内容是unbox float x - 编译器将其视为(unbox float) x。结果这两个函数实际上是这样处理的:

let intToFloat (x:int<'u>) : float<'u> = 
  let f = unbox float in f x

let float32ToFloat (x:float32<'u>) : float<'u> = 
  let f = unbox float in f x

所以,你正在使用float函数,将它(不安全地)转换为另一种类型的函数,然后调用它。第一种情况下的类型为int<'u> -> float<'u>,第二种情况下为float32<'u> -> float<'u>

我认为第一个可行,因为当编译器看到float函数(没有任何类型注释)时,它默认为int -> float,并且由于测量单位在运行时被擦除,这可能是转换为int<'u> -> float<'u>(但不转换为第二种类型 - 因为您正在执行不安全的转换)。

因此,主要问题是您的实现中的错误括号(这会导致一个非常微妙的问题)。我想你可能想要这样的东西:

let intToFloat (x:int<'u>) : float<'u> = unbox (float x)
let float32ToFloat (x:float32<'u>) : float<'u> = unbox (float32 x)