对于此代码:
module Module =
let func x y z = 0
[<EntryPoint>]
let main args =
func 1
func 1 1
0
反编译结果:
[CompilationMapping(SourceConstructFlags.Module)]
public static class Main
{
[CompilationMapping(SourceConstructFlags.Module)]
public static class Module
{
[Serializable]
internal sealed class main@30 : OptimizedClosures.FSharpFunc<object, object, int>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
public int x;
[CompilerGenerated]
[DebuggerNonUserCode]
internal main@30(int x)
{
this.x = x;
}
public override int Invoke(object y, object z)
{
return func(x, y, z);
}
}
[Serializable]
internal sealed class main@31-1 : FSharpFunc<object, int>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
public int x;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
public int y;
[CompilerGenerated]
[DebuggerNonUserCode]
internal main@31-1(int x, int y)
{
this.x = x;
this.y = y;
}
public override int Invoke(object z)
{
return func(x, y, z);
}
}
[CompilationArgumentCounts(new int[]
{
1,
1,
1
})]
public static int func<a, b, c>(a x, b y, c z)
{
return 0;
}
[EntryPoint]
public static int main(string[] args)
{
int x = 1;
new main@30(x);
int x2 = 1;
int y = 1;
new main@31-1(x2, y);
return 0;
}
}
public static a Dump<a>(a arg00)
{
return arg00.Dump();
}
}
它生成一个具体的类型,即类型定义中提供了通用参数。为什么在构造时不这样做?我还注意到类型是在发生调用的模块中生成的,而不是在定义func
的地方生成的。
有了let func x y z = ...
,我们需要类型的实现来涵盖所有可能性:
FSharpFunc<T1,FSharpFunc<T2,T3,TReturn>>
FSharpFunc<T1,T2,FSharpFunc<T3,TReturn>>
FSharpFunc<T1,FSharpFunc<T2,FsharpFunc<T3,TReturn>>>
编译器可以在定义函数的同一位置生成所有可能的组合,仅对具有推断类型的参数关闭。
您可能会争辩说,对于7个参数的列表,类型的集合会非常大,但是FSharpFunc<T1,T2,..,Tn, FSharpFunc<...>>
之类的类型仅仅是一个优化。并且FSharpFunc
支持多达六种泛型类型,然后编译器必须切换到FSharpFun<T1,T2,T3,T4,T5,FSharp<...>>
。
答案 0 :(得分:3)
正如Fyodor所指出的那样,不是函数创建使编译器生成隐藏类。隐藏的类用于实现部分应用程序。
在F#中,部分应用程序和lambda被实现为编译器生成的类,该类扩展了抽象类。 C#lambda依赖于委托。由于JVM没有委托,因此IIRC Java和Scala使用与F#类似的技术。
我怀疑F#编译器会为每个部分应用程序生成一个类,因为它比收集所有部分应用程序并合并相同的应用程序更简单。
它还有助于F#程序的可调试性,因为该名称暗示了部分应用程序的完成位置:main@31-1
=>在第31行的main函数中。此名称(如果包含在日志或性能运行中)可以帮助识别什么内容。部分应用会引起问题。
这是以增加P#汇编文件的大小为代价的,如Pavel的评论所述。