SRTP member constraits can't see raw fields of C# classes?

92 Views Asked by At

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:

  1. F#-defined classes are compatible with SRTP member constraints
  2. C#-defined classes with properties are compatible, too
  3. 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...

0

There are 0 best solutions below

Related Questions in F#