如何将 F# 函数从 C# 应用程序传递到另一个 F# 函数?

发布于 2024-11-07 16:06:54 字数 663 浏览 0 评论 0 原文

我有 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

我有一个 C# 控制台应用程序,它引用 F# 库并尝试执行以下操作:

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>

I have F# class library assembly that contains two functions:

let add a b = a + b

and

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

I have a C# console application which references the F# library and is attempting to do the following:

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

However, the compiler complains that [myFsLibrary.add] cannot be converted from 'method group' to FSharpFunc<int, FSharpFunc<int, int>>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

云朵有点甜 2024-11-14 16:06:54

其他人已经提供了答案,但我只想说你不应该这样做。

不要将 F# 列表公开给 C#。不要向 C# 公开柯里化函数。阻抗不匹配在此边界处是可见的,因此最好在跨语言汇编边界处公开通用框架类型。请参阅

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

获取更多建议。

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.

海夕 2024-11-14 16:06:54

您可以使用 FSharpFunc 委托显式创建函数。在 C# 中,创建将所有参数作为元组的函数会更方便,因此您可以这样做,然后使用 FuncConvert 将函数转换为柯里化类型。例如:

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

但是,如果您需要从 C# 代码调用某些 F# 函数,建议公开具有 C# 友好接口的函数。在这种情况下,您可以使用 Func 委托,并且第一个参数应该是 IEnumerable 而不是 F# 特定的列表类型:

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

然后您的 C# 应用程序可以使用:

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

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));
未蓝澄海的烟 2024-11-14 16:06:54

原因是 add 被导出为普通的 .Net 风格函数,并具有粗略的

int add(int, int)

C# 签名,并且大多数 .Net 语言将其视为采用 2 个 int 参数并返回单个 <代码>int值。但 F# 并不以这种方式看待函数。相反,它将 add 视为一个函数,它接受一个 int 并返回一个函数,该函数又接受一个 int 并返回一个 int.这种函数视图使得实现柯里化等操作变得非常容易。

为了从 C# 世界观转换为 F# 世界观,您需要使用一些魔法将方法折叠到自身上。我通过定义一组 F# 工厂和扩展方法来实现这一目标,为我带来神奇的效果。例如,

[<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

我可以使用此库为 add 方法获取适当的 F# 委托类型,如下所示

var del = FSharpFuncUtil.Create<int, int, int>(myFsLibrary.add);

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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文