Pregunta

Tengo un conjunto de biblioteca de clase F # que contiene dos funciones:

let add a b = a + b

y

let rec aggregateList list init (op:int -> int -> int) =
    match list with
    |[] -> init
    |head::tail ->
        let rest = aggregateList tail init op
        op rest head

Tengo una aplicación C # Consola que hace referencia a la biblioteca F # y está intentando hacer lo siguiente:

FSharpList<int> l = new FSharpList<int>(1, new FSharpList<int>(2, FSharpList<int>.Empty));
int result = myFsLibrary.aggregateList(l, 0, myFsLibrary.add);

Sin embargo, el compilador se queja de que [MyFSLIBRARY.ADDD] no se puede convertir del 'grupo de métodos' a FSharpFunc<int, FSharpFunc<int, int>>

¿Fue útil?

Solución

You can explicitly create a function using the FSharpFunc delegate. In C#, it is more convenient to create function that takes all arguments as a tuple, so you can do that and then convert the function to a curried type using FuncConvert. Something like:

FuncConvert.FuncFromTupled(new FSharpFunc<Tuple<int, int>, int>(args => 
    arags.Item1 + args.Item2))

However, if you need to call some F# function from your C# code, it is recommended to expose a function with a C#-friendly interface. In this case, I you can use Func delegate and the first argument should be IEnumerable instead of F#-specific list type:

module List = 
    let AggregateListFriendly inp init (op:Func<int, int, int>) =
        aggregateList (List.ofSeq inp) init (fun a b -> op.Invoke(a, b))

Then your C# appplication can just use:

List.AggregateListFriendly(Enumerable.Range(0, 10), 0, (a, b) => a + b));

Otros consejos

Other people have provided answers, but I'll just step in to say that you shouldn't do this.

Don't expose F# lists to C#. Don't expose curried functions to C#. The impedance mismatch is visible at this boundary, so it is better to expose common framework types at cross-language assembly boundaries. See

http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/fsharp-component-design-guidelines.pdf

for more advice.

The reason why is that add is exported as a normal .Net style function and has the rough signature

int add(int, int)

C#, and most .Net languages, see this as a method which takes 2 int parameters and returns a single int value. F# though doesn't see functions this way. Instead it sees add as a function takes an int and returns a function which in turn takse an int and returns an int. This view of functions makes it very easy to implement operations like currying.

In order to convert from the C# view of the world to F# you need to do a bit of magic to fold a method onto itself. I accomplish this by defining a set of F# factory and extension methods to do the magic for me. For example

[<Extension>]
type public FSharpFuncUtil = 

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

    static member Create<'a,'b,'c> (func:System.Func<'a,'b,'c>) = 
        FSharpFuncUtil.ToFSharpFunc func

I can use this library to get the appropriate F# delegate type for the add method like so

var del = FSharpFuncUtil.Create<int, int, int>(myFsLibrary.add);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top