It's easy enough to convert from a measurement unit to the underlying value type and back:
[<Measure>]
type meter
module Meter =
let from m = m * 1</meter>
let too m = m * 1<meter>
But then I thought, wouldn't it be lovely to have a conversion from one underlying type to the measurement unit and back, generically? Sure, I can on each instance be specific about it but I figured it'd be convenient to have a cast-to and cast-from operator, while retaining type safety.
My first attempt:
[<AutoOpen>]
module Convert =
let inline (!->) x = x * 1<_> // (often) magically works
let inline (!-<) x = x * 1</_> // syntax error on the "/_"
The !->
operator works by virtue of type inference. It would be nice if the second operator also worked, but /_
is not supported syntax. I tried some type-annotated variants but couldn't come up with a straight generic solution, in part because we cannot write 'T<'M>
or some variant thereof.
I then thought of using the trick used for implicit conversions, but then applied to explicit conversions, since the underlying type of 'T<'M>
if 'T
and an explicit cast always exists:
[<AutoOpen>]
module Convert =
let inline explicit_convert (x:^a) : ^b = ((^a or ^b) : (static member op_Explicit : ^a -> ^b) x)
let inline (::>) x = explicit_convert x // works both ways
This has the added benefit that it works on any type that has an explicit conversion operator. Unfortunately, the compiler complains that it may not be safe to do so:
Member constraints with the name 'op_Explicit' are given special status by the F# compiler as certain .NET types are implicitly augmented with this member. This may result in runtime failures if you attempt to invoke the member constraint from your own code.
I'm not sure if I'm on the right track or whether there's a more type-safe way of creating an operator that can go from any measurement to the underlying type and back (including complex measurements, like int<meter ^ 4>
or int<meter / sec ^ 2>
)