I was surprised by the "The type 'Lib1.Class1' does not support the operator 'get_Id'"
error below:
F# Project Lib0.fsproj
namespace Lib0
type T0 =
val Id: string
new s={Id=s}
type T1 =
val mutable Id: string
new s={Id=s}
C# Project Lib1.csproj
namespace Lib1;
public class Class1
{
public string Id;
public Class1(string id) {
Id = id;
}
}
public class Class2
{
public string Id { get; set; }
public Class2(string id) {
Id = id;
}
}
F# Project Test1.fsproj
module Lib =
type T2 =
val Id: string
new s={Id=s}
type T3 =
val mutable Id: string
new s={Id=s}
let inline f<'T when 'T: (member Id: string)> (x: 'T) = x.Id
f (Lib0.T0("Lib0.T0")) |> printfn "%s" // ok
f (Lib0.T1("Lib0.T1")) |> printfn "%s" // ok
f (Lib.T2("Lib.T2")) |> printfn "%s" // ok
f (Lib.T3("Lib.T3")) |> printfn "%s" // ok
// f (Lib1.Class1("Class1")) |> printfn "%s" // The type 'Lib1.Class1' does not support the operator 'get_Id'
f (Lib1.Class2("Class2")) |> printfn "%s" // ok
Really?? Or did I miss anything obvious??
To clarify, the disappointing observations I made here are:
- F#-defined classes are compatible with SRTP member constraints
- C#-defined classes with properties are compatible, too
- C#-defined classes with raw fields are NOT compatible, which is news to me
If Rider's IL viewer didn't fail me, at least T1 should expose a raw string field of it without any wrapping properties. Then what might be the difference between T1 vs Lib1.Class1? Do F#-defined classes have extra metadata (for inlining?) that make a difference here?
(Note: using the pre-F# 7 SRTP syntax (^T) didn't seem to help.)
Background: I need to deal with a bunch of third party C# classes that happen to have a common public string field (not property) Id, e.g. https://github.com/pulumi/pulumi-aws/blob/master/sdk/dotnet/S3/GetBucket.cs#L230
If this is a real restriction of SRTP, I guess I'll have to insert a wrapper type here and there in my code. That won't be the end of the world but will clutter the code...