Bounds / Domain on Units of Measure
I find the idea of F# Units of Measure very appealing. However, it is often the case that some units have a particular domain they live in. For instance, a distance is a positive number, a temperature is greater than the Kelvin zero, a probability is between 0 and 1, and so forth - but I haven't seen anything built in to represent that no开发者_Python百科tion, and validate that a particular value is a valid measure for a specific unit. Does Unit of Measure support something like this (I do not think so), and if not, is there a recommended way to implement that behavior?
Units of Measure in F# do not support behaviors. They are a static mechanism used during compilation to throw type errors. You're going to need an object to encapsulate any "behavior". For example you could make a type Temperature
that provides operators for bound checking. This object could then throw an exception if you passed it -1.0<Kelvin>
.
You could do something like this.
[<Measure>]
type Kelvin =
static member ToCelsius kelvin =
(kelvin - 273.15<Kelvin>) * 1.0<Celsius/Kelvin>
and [<Measure>] Celsius =
static member ToKelvin celsius =
(celsius + 273.15<Celsius>) * 1.0<Kelvin/Celsius>
type Temperature(kelvin : float<Kelvin>) =
do
if kelvin < 0.0<Kelvin> then
failwith "Negative Kelvin Temperature"
member this.Celsius with get() = Kelvin.ToCelsius kelvin
member this.Kelvin with get() = kelvin
// could add operators here like (=) or (+)
let good = Temperature(0.0<Kelvin>)
let bad = Temperature(-1.0<Kelvin>)
As gradbot already said, F# units of measure are only used at compile time. Unfortunatelly, you cannot write condition that values of certain unit have to be e.g. larger than zero (this would be pretty difficult to check in the compiler).
Since units of measure also don't exist at run-time you cannot write generic function that takes any temperature and checks whether a value has unit Kelvin
and is smaller than zero (and then throw an exception). You'd have to write different wrapper types (e.g. TemperatureK
for values of type float<Kelvin>
).
A better option may be to use a library that tracks units of measure at runtime. Then you can get the unit at runtime and implement the checking. Phil Trelford implemented a nice runtime units of measure library that may be worth checking out.
精彩评论