F# and MEF: Exporting Functions
Asked Answered
A

1

2

So, I was trying to get this simple test working in an F# console app:

open System.Reflection
open System.ComponentModel.Composition
open System.ComponentModel.Composition.Hosting

[<Export(typeof<int -> string>)>]
let toString(i: int) = i.ToString()

[<EntryPoint>]
let main argv = 
    use catalog = new AssemblyCatalog(Assembly.GetEntryAssembly())
    use container = new CompositionContainer(catalog)

    let myFunction = container.GetExportedValue<int -> string>()
    let result = myFunction(5)
    0

I expected MEF to get the function properly resolved, but it doesn't. Instead, I get this:

An unhandled exception of type 'System.ComponentModel.Composition.CompositionContractMismatchException' occurred in System.ComponentModel.Composition.dll

Additional information:

Cannot cast the underlying exported value of type 'Program.toString (ContractName="Microsoft.FSharp.Core.FSharpFunc(System.Int32,System.String)")' to type 'Microsoft.FSharp.Core.FSharpFunc``2[System.Int32,System.String]'.

  • What am I missing here?
  • What is the difference between FSharpFunc(System.Int32, System.String) and FSharpFunc``2[System.Int32, System.String]?
  • What is the correct way to import/export F# functions via MEF?
Austronesia answered 21/2, 2016 at 23:39 Comment(0)
A
3

The compiler turns top-level F# functions into methods, so your example will be compiled as:

[Export(FSharpFunc<int,string>)]
public string toString(int i) { return i.ToString(); }

This is probably causing the error. You can force the compiler to produce a property getter of FSharpFunc type by calling some operation that returns a function - even a simple identity function will don:

let makeFunc f = f 

[<Export(typeof<int -> string>)>]
let toString = makeFunc <| fun (i:int) -> 
  i.ToString()

I have not tested this, but I think it could work. That said, it is probably safer to go with a simple single-method interface in this case.

Auvergne answered 22/2, 2016 at 0:34 Comment(7)
Thanks for your response. I'm precisely trying to get rid of interfaces. I figured most of my code only needs the actual functions, not the containing types, or any interface stuff.Austronesia
Also: you mentioned this is only for top-level functions, right? what if I put all my [<Export>]ed functions as nested functions?Austronesia
...Argh.. Right. I can't apply attributes to nested functions. Is there any MEF-like library that will play nicer with F#, while still be able to be consumed by C# code?Austronesia
Alternatively, I think you should be able to use a signature file (and signature (int -> string) instead of int -> string) to achieve the same result.Helprin
@Helprin can you elaborate on that?Austronesia
For the record, using Func<T> and Action<T> works just fine, though it's not as elegant as using native F# function syntaxAustronesia
@HighCore see section 11.2.1.1 of the F# spec.Helprin

© 2022 - 2024 — McMap. All rights reserved.