Units of Measure, Interfaces and Mixins
Consider the 开发者_如何学Cfollowing F# code:
type ILinear =
interface
end
type IMetric =
interface
end
[<Measure>] type cm =
interface ILinear
interface IMetric
[<Measure>] type m =
interface ILinear
interface IMetric
[<Measure>] type time
I want to use these interfaces as a means of both grouping the types of the measures and as a way of allowing a level of genericness between "any measure" and "a specific measurement"--something akin to:
(* Yes, I know this syntax is probably incorrect *)
let rate (distance:float<'u:#ILinear,#IMetric>) (time:float<time>) =
distance/time
I realize this is probably pushing the limits of possibility but I'm just kind of curious if this is possible and if so, what the syntax would be. As I say, this is using interfaces as sort of a poor man's mixin.
I don't think this is possible, but I quite like the idea :-).
If it was possible, then the constraints would be probably written using the same syntax that you can use to write interface constraints for ordinary (non-measure) type parameters:
let rate<[<Measure>] 'u when 'u :> IMetric> (distance:float<'u>) (time:float<time>) =
distance/time
The error message clearly says that constraints can be only specified on ordinary type parameters (actually, I was even surprised that units of measure can implement interfaces - it doesn't look very useful as they are completely erased during the compilation):
error FS0703: Expected type parameter, not unit-of-measure parameter
The best workaround that I can think of is to write a simple wrapper that stores a value (with some unit) and additional (phantom) type that represents the constraints:
[<Struct>]
type FloatValue<[<Measure>] 'u, 'constr>(value:float<'u>) =
member x.Value = value
let cm f = FloatValue<_, IMetric>(f * 1.0<cm>)
The cm
function takes a float and wraps it into a FloatValue
. The second type argument is an ordinary type argument, so it can be provided with some type that implements interfaces (or with just a single interface). The rate
function then looks like this:
let rate (distance:FloatValue<'u, #IMetric>) (time:float<time>) =
distance.Value / time
Since the constraints cannot be specified on a unit type, we have to specify them on the second type argument. You can then call the function using:
rate (cm 10.0) 5.0<time>
精彩评论