Maintaining Units of measure across type converstions

女生的网名这么多〃 提交于 2019-12-01 08:45:22

问题


If we define a unit of measure like:

[<Measure>] type s

and then an integer with a measure

let t = 1<s>

and then convert it to a float

let r = float t

we see that r = 1.0 without a measure type. This seems very odd, as all the measure information has been lost.

You can use LanguagePrimitives.FloatWithMeasure to convert back to a float with something like

let inline floatMeasure (arg:int<'t>) : (float<'t>) =
    LanguagePrimitives.FloatWithMeasure (float arg)

which enforces the right types, but this doesn't feel like the right solution as the docs for units of measure (http://msdn.microsoft.com/en-us/library/dd233243.aspx) say

However, for writing interoperability layers, there are also some explicit functions that you can use to convert unitless values to values with units. These are in the Microsoft.FSharp.Core.LanguagePrimitives module. For example, to convert from a unitless float to a float, use FloatWithMeasure, as shown in the following code.

Which seems to suggest that the function should be avoided in F# code.

Is there a more idiomatic way to do this?


回答1:


(Caveat: I've not used units much in anger.)

I think that the only negative for using e.g. FloatWithMeasure is the unit-casting aspect (unitless to unitful). I think this is conceptually orthogonal to the numeric-representation-casting aspect (e.g. int to float). However there is (I think) no library function to do numeric-representation-casting on unit-ful values. Perhaps this is reflective of the fact that most unitful values model real-world continuous values, as so discrete representations like int are typically not used for them (e.g. 1<s> feels wrong; surely you mean 1.0<s>).

So I think it's fine to 'cast representations' and then 'readjust units', but I wonder how you got the values with different representations in the first place, as it's often typical for those representations to be fixed for a domain (e.g. use float everywhere).

(In any case, I do like your floatMeasure function, which un-confounds the unit-aspect from the representation-aspect, so that if you do need to only change representation, you have a way to express it directly.)




回答2:


Here's working snippet that does exactly what you need although gives warning

stdin(9,48): warning FS0042: This construct is deprecated: it is only for use in the F# library)):

[<NoDynamicInvocation>]
let inline convert (t: int<'u>) : float<'u> = (# "" t : 'U #)

[<Measure>] type s
let t = 1<s>
let t1 = convert t // t1: float<s>

However, I wouldn't suggest this approach.
First of all, UoM are compile-time, while type conversion let r = float t is runtime. At the moment of invocation, int -> float has no idea of whether it is int<s> or int<something_else>. So it is simply unable to infer a proper float<'u> at runtime.

Another thought is that philosophy behind UoM is wider than it's described. It is like saying the compiler, "well, it is int, but please treat it as int<s>". The goal is avoiding occasional improper use (e.g., adding int<s> to int<hours>).
Sometimes it makes no sense of int -> float conversion: think of int<ticks>, there is no sense of float<ticks>.

Further reading, credits to @kvb for pointing on this article.



来源:https://stackoverflow.com/questions/9406030/maintaining-units-of-measure-across-type-converstions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!