Interop of delegate style types between F# and other .Net languages is a pain point that results from a fundamental difference in how delegates are represented in the F# language. Typical .Net languages like C# see a delegate as taking 0 to N parameters and potentially returning a value. F# represents all exposed delegates as taking and returning a single value via the FSharpFunc<T,TResult> type. Multiple parameters are expressed by having the TResult type be yet another FSharpFunc<T,TResult>.

This creates a host of problems calling exposed F# delegate / function types

  • Can’t use C# lambda expressions which take more than one parameter
  • System.Func<> expressions of equivalent logical shape can’t be converted
  • Method group conversions don’t work

The only step is to introduce a conversion step when calling into F# code. F# does provide a helper method in F# in the FSharpFunc.FromConverter method. But once again it’s only useful for delegates of one parameter, anything higher requires a more in depth conversion. For example here is the C# code to convert a 2 parameter delegate into the equivalent F# type.

public static FSharpFunc<T1, FSharpFunc<T2,TResult>> Create<T1, T2, TResult>(Func<T1,T2,TResult> func)
{
    Converter<T1, FSharpFunc<T2, TResult>> conv = value1 =>
        {
            return Create<T2,TResult>(value2 => func(value1, value2));
        };
    return FSharpFunc<T1, FSharpFunc<T2, TResult>>.FromConverter(conv);
}

Not very pretty or intuitive because the code needs to recreate the nested style of F# func’s. This gets even more tedious and error prone as it gets past 2 parameters.

The simplest solution, as is true with most F# interop scenarios, is to leverage F# itself to define the interop / conversion layer. It already naturally creates the proper nesting structure so why spend type redoing the logic in C#. The logic can then be exposed as a set of factory and extension methods to make it easily usable from C#.

    [<Extension>]
    type public FSharpFuncUtil = 

        [<Extension>] 
        static member ToFSharpFunc<'a,'b> (func:System.Converter<'a,'b>) = fun x -> func.Invoke(x)
    
        [<Extension>] 
        static member ToFSharpFunc<'a,'b> (func:System.Func<'a,'b>) = fun x -> func.Invoke(x)
    
        [<Extension>] 
        static member ToFSharpFunc<'a,'b,'c> (func:System.Func<'a,'b,'c>) = fun x y -> func.Invoke(x,y)
    
        [<Extension>] 
        static member ToFSharpFunc<'a,'b,'c,'d> (func:System.Func<'a,'b,'c,'d>) = fun x y z -> func.Invoke(x,y,z)
    
        static member Create<'a,'b> (func:System.Func<'a,'b>) = FSharpFuncUtil.ToFSharpFunc func
    
        static member Create<'a,'b,'c> (func:System.Func<'a,'b,'c>) = FSharpFuncUtil.ToFSharpFunc func
    
        static member Create<'a,'b,'c,'d> (func:System.Func<'a,'b,'c,'d>) = FSharpFuncUtil.ToFSharpFunc func

Now I?? can convert instances of System.Func<> to the F# equivalent by simply calling .ToFSharpFunc().

var cmd = Command.NewSimpleCommand(
    name,
    flagsRaw,
    func.ToFSharpFunc());

Much better.


Share Post

Google+

comments powered by Disqus