开发者

Units of Measure, Interfaces and Mixins

开发者 https://www.devze.com 2023-03-25 22:50 出处:网络
Consider the 开发者_如何学Cfollowing F# code: type ILinear = interface end type IMetric = interface

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>
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号