I'm curious about the performance enhancements that have been made for FSharpFunc<>. Is it the fact that it does not contain multiple delegate so there is no need to loop over all the references when firing a function call ? Anything else ?
问题:
回答1:
I think that the primary motivation for using FSharpFunc<>
rather than Func<>
or any other delegate is that you cannot create a class that would inherit from a delegate type (at first, this sounds reasonable, but in .NET, delegate is actually just some special class, so it may be in principle possible to allow this). Why is this needed?
If you write a function in F# then it is (in a relatively few, but quite important cases) treated in a curried form. For example int -> int -> int
is actually a function type int -> (int -> int)
(currying means that you write a function using just functions of single parameter - if you call it with the first argument, you'll get a function as a result and you can invoke the returned function with the second argument).
If F# used delegates, the type would be something like Func<int, Func<int, int>>
. As Brian mentioned, the invocation f x y
would be translated into two invocations: f(x)(y)
. This kind of invocation is however the most common (specifying just a single argument is called partial function application). So, when F# compiles a function like this, it creates an inherited class with an optimized invoke method, so that it can be invoked as f.Invoke(x, y)
:
class @some_F#_name@ : Func<int, Func<int, int>> {
public int Invoke(int arg1, int arg2) { /* optimized call */ }
}
Unfortunately, it isn't possible to create such class by inheriting from standard Func
(because it is a delegate), so F# has to declare its own type which can be used as a base class...
回答2:
(I think they're now called FSharpFunc
rather than FastFunc
.)
It's represented as a type with a single abstract method (Invoke), which I think avoids some of the overheads you get with true delegates. And for multiple curried parameters, it enables you to call with all the parameters 'at once' rather than one-by-one (e.g. so that f x y
can be invoked on the CLR as f(x,y)
rather than f(x)(y)
.
Is there anything else? I don't recall right now. You can check out the source code in prim-types.fs in FSharp.Core in the source distribution that comes with the CTP release.