0

I've just written a simple method that C# can see but calling it (even with valid arguments) throws up at run-time.

example that fails at runtime:

F#:

namespace Library1
type Class1() = 
  member __.Foo = "F#"
module MyModule = 
  // fails at run-time
  let inline fOnly (x:^a) : string = (^a:(member Foo: _) x)
  // works from C# and F# so I know it's not a problem with my stp
  let testFOnly () = fOnly (Class1())

C# consumer:

namespace ConsoleApplication1
  {
    class Program
    {
      var class1 = new Library1.Class1();
      // NotSupportedException
      var result = Library1.MyModule.fOnly(class1);
      Console.ReadLine();    
    }
  }

Why does this compile, then fail at run-time? Am I doing something wrong or should I just assume any attempt to call an stp method from C# will always fail? Should I then attempt to hide them from C#?

Community
  • 1
  • 1
Maslow
  • 18,464
  • 20
  • 106
  • 193

2 Answers2

2

The use of inline in F# doesn't actually produce a function in the output assembly.

The function is encoded in some metadata that only the F# compiler can read and then insert inline.

As a result, you cannot call inline functions from C#

John Palmer
  • 25,356
  • 3
  • 48
  • 67
  • 1
    the c# to call this compiles. Also the following compiles and works `let inline add x y = x + y` called from C# as `var inlinedAdd = Library1.MyModule.add(1,2);` so apparently you can call `inline` functions from C# ? – Maslow Feb 02 '16 at 21:40
  • 1
    @Maslow - this surprises me - maybe the compiler creates a backup non-inline version where it can? – John Palmer Feb 02 '16 at 21:45
  • what suprises me is people upvoted an answer that it took a simple add function to disprove. – Maslow Feb 02 '16 at 21:46
2

The code in the example is wrong

Class1 has the member Foo not X and it fails to compile

-------------- After Question Edit ------------

You can use ILSpy to see the decompiled code and the source of the error

Decompiled C# -

namespace Library1
{
    [CompilationMapping(SourceConstructFlags.Module)]
    public static class MyModule
    {
        public static string fOnly<a>(a x)
        {
            "Dynamic invocation of get_Foo is not supported";
            throw new NotSupportedException();
        }

        public static string testFOnly()
        {
            Class1 @class = new Class1();
            return "F#";
        }
    }
}

But you can see from the code that this will work

static void Main ( string [ ] args )
{
  Console.WriteLine( Library1.MyModule.testFOnly()) ;
  Console.ReadLine();  
}

There's also no reason to make this a function call let testFOnly () = fOnly (Class1()) instead of a regular binding since it's targeting an immutable property of the class.

i.e. let testFOnly = Class1() |> fOnly

cloudRoutine
  • 370
  • 3
  • 6
  • my bad, adjusted only half the F# to make it slightly more readable for an SO question, updated question – Maslow Feb 02 '16 at 21:49
  • I'm really surprised it doesn't embed a call to the member as inlined, and instead grabs the compile-time value, maybe it would do something more interesting/intuitive if it was pointed at a function instead of a prop/field with a constant value – Maslow Feb 03 '16 at 14:15