I have defined a record type with a unit of measure in a common library (LibRoot) that is used by both C# (LibC) and F# (LibF) code.
I then wrote a public API in the C# library (LibC) that the F# library (LibF) consumes. But any time i attempt to pass an object to this API F# complains that it must have a unit of measure.
//LibRoot - F#
type Vec2<[Measure] 'u> = {
X : int
Y : int
}
//LibC - C#
public static class Funcs
{
public void DoWork(Vec2 vec) { //no measure needed in C#
....
}
}
//LibF - F#
open LibC
let myVec : Vec2<1> = { X = 123; Y = 456 }
DoWork(myVec) //FS0001
error FS0001: Type mismatch. Expecting a 'Vec2' but given a 'Vec2<1>' The tuples have differing lengths of 0 and 1
I've tried:
Vec2<1>
: FS0001 Vec2<0>
: Invalid Literal in type Vec2<()>
: Unexpected ')' in type Vec2<_>
: FS0001 Vec2<unit>
: Expected unit of measure, not type (Vec2) myVec
: No constructors available for type 'Vec2<'u> Does anybody know a way to construct a measured record type in an interop friendly way?
As mentioned in the comment, units of measure are F#-only feature that has no representation in the compiled .NET code, so when you use a unit-annotated type from C#, it will appear as a type without units.
The compiled C# code will not contain special F# meta-data to indicate that this is a unit-annotated type and so the F# compiler referencing your C# library does not recognise it as unit-annotated type. Arguably, the F# compiler could be smarter and figure this out (because it can figure out that the Vec2
type is originally coming from F#).
I don't think there is a safe way of converting un-measured Vec2
to measured Vec2<_>
but the unsafe conversion using unbox
will work at runtime:
let v : LibRoot.Vec2<1> = { LibRoot.Vec2.X = 1; Y = 2 }
LibCs.Funcs.DoWork(unbox v)
I don't think there is a way of referring to the type Vec2
(without measure) explicitly in F# code, but unbox
infers the type fine. This is not very nice, so if you need this conversion often, it's probably better to redesign your library so that C# uses a separate type without units.
Tomas' answer is correct. I'll share my workaround for posterity.
My measure units were
[<Measure>] type Absolute
[<Measure>] type Relative
I introduced 'concrete' record types that were convertible to Vec2<>
for interop. For example
type Offset2 = {
X : int
Y : int
}
with
static member ofVec (vec : Vec2<Relative>) : Offset2 = { X = vec.X; Y = vec.Y }
static member toVec (off: Offset2) : Vec2<Relative> = { X = off.X; Y = off.Y }
I convert them into Vec2's to perform math and but use the 'concrete' types for public APIs. It is a little annoying to have the extra types but it works.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.