为什么F#中的函数以这种方式实现?

时间:2013-02-08 07:36:54

标签: f#

F#功能与普通CLR方法有很大不同,因为支持currying。例如功能

let inc a = a + 1

将具有Microsoft.FSharp.Core.FSharpFunc<int,int>类型。它会产生C#互操作性问题。必须专门设计函数才能从C#中轻松调用。

这种设计背后的理由是什么?我相信原因在于支持。但是可以使用闭包来实现currying。例如这段代码:

let add a b = a + b
let inc = add 1

可以很容易地由编译器转换成这个:

let add a b = a + b
let inc = fun x -> add 1 + x

在这种情况下,add和inc都可以是普通System.Func个对象。我相信这个设计决定背后有一些有趣的原因。

3 个答案:

答案 0 :(得分:10)

据我记忆,在F#中为函数设置单独类型的动机是性能(在早期版本FSharpFunc<...>中实际上称为FastFunc<...>)。我不完全确定最近的发展(我确信F#团队做了一些测试,以确定代理是否可以在Visual Studio 2010中工作),但这是我如何理解这个问题:

如果您有一个函数add : int -> int -> int,那么该函数可以表示为委托Func<int, Func<int, int>>(使用咖喱表示)。问题是,您经常需要使用add 1 2等参数调用它。

使用带有嵌套Func类型的表示,这将编译为add.Invoke(1).Invoke(2)

但是,在编译add之类的函数时,F#编译器实际上创建了一个新类,比如AddClass,其中从<{1}} 继承了使用两个参数添加额外的FSharpFunc<int, FSharpFunc<int, int>>重载。这意味着,在大多数情况下,Invoke可以编译为只有一个调用add 1 2

这种设计使F#代码更快。它使互操作性稍微复杂化,但不是太多。编写一个带代理的F#成员或函数相当容易:

add.Invoke(1, 2)

(您只需要添加类型注释然后调用let foo (inc : Func<int, int>) = inc.Invoke(41) - 但您也可以使用f.Invoke作为第一类值并将其传递给其他函数)

答案 1 :(得分:1)

我的猜测是F#表示支持大步语义,而System.Func则不支持。

答案 2 :(得分:1)

与C#互操作的最佳方式是将所有内容包装在类/成员中。 C#永远不必看到成员的内部运作。