Our primary stack at Innova is .NET, that we principally historically implemented in C# (and some Visual Basic.NET)

In some of our newest products and research, we have adopted F# as it very elegantly solves a number of problems that lend themselves well to a functional solution.

Code from either side is freely usable in the other, and we have not had any issues doing interop.

Until recently.

With some new work we’re doing using .NET 5, I ran into this error:

Error Message:
   System.InvalidProgramException : Common Language Runtime detected an invalid program.

This was from the F# side, when running some tests.

The (simplified) class itself, written in C#, was this:

public class Lion
    public string Name { get; init; }

The test, written in F# was this:

let ``Lion Is Created Correctly`` () =
    let simba = Lion(Name = "Simba")
    simba |> should not' (be null)

At first I thought the problem was with the interop itself. But this class behaved correctly

public class Bear
    public string Name { get; set; }

The test is this:

let ``Bear Is Created Correctly`` () =
    let yogi = Bear(Name = "Yogi")
    yogi |> should not' (be null)

This one run successfully.

This class, a record, also triggered the error.

public record Tiger
    public string Name { get; init; }

What do the classes erroring out have in common? The init keyword.

The init keyword allows you to set a property only once, upon initialization of the class. It becomes read-only after that.

It turns out the root of the problem is the C# and F# teams had not aligned around the implementation of the init keyword. You can read more about the problem here.

The bottom line is that this can only be fixed with a change to the compiler.

To solve this problem in the interim, I rewrote the classes to have readonly public properties and set them in a constructor.

public class Snake
    public string Name { get; }
    public Snake(string name) => Name = name;

The test in F# passes successfully.

let ``Snake Is Created Correctly`` () =
    let steven = Snake(name = "Steven")
    steven |> should not' (be null)

The code is in my Github.

Happy hacking!