C # 앱에서 다른 F # 함수로 F # 기능을 어떻게 전달합니까?
-
14-11-2019 - |
문제
두 가지 기능이 포함 된 F # 클래스 라이브러리 어셈블리가 있습니다.
let add a b = a + b
.
및
let rec aggregateList list init (op:int -> int -> int) =
match list with
|[] -> init
|head::tail ->
let rest = aggregateList tail init op
op rest head
.
F # Library를 참조하고 다음을 수행하려고 시도하는 C # 콘솔 응용 프로그램이 있습니다.
FSharpList<int> l = new FSharpList<int>(1, new FSharpList<int>(2, FSharpList<int>.Empty));
int result = myFsLibrary.aggregateList(l, 0, myFsLibrary.add);
.
그러나 컴파일러는 'myfslibrary.add]에서'메소드 그룹 '에서 FSharpFunc<int, FSharpFunc<int, int>>
로 변환 할 수 없음을 불평합니다.
해결책
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));
다른 팁
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
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);