代码之家  ›  专栏  ›  技术社区  ›  Johannes Rudolph

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

  •  1
  • Johannes Rudolph  · 技术社区  · 11 年前

    这是在我考虑以下问题时出现的: F# Unit of Measure, Casting without losing the measure type 请注意,我不是在尝试使用这个开箱密码,我只是在回答问题时发现了一些奇怪的行为。

    为什么以下代码有效

    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 回复  |  直到 7 年前
        1
  •  3
  •   Tomas Petricek    11 年前

    代码片段中的微妙之处在于 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> 在第二种情况下。

    我认为第一种方法有效,因为当编译器看到 浮动 函数(没有任何类型注释),它默认为 int -> float 而且,由于度量单位在运行时被擦除,因此可以将其转换为 整数<'u>->浮动<'u> (但不能转换为第二种类型,因为您正在执行不安全的强制转换)。

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

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