为什么F#编译器更喜欢生成FSharpFunc类型的封闭实现?

时间:2018-06-30 17:36:19

标签: f#

对于此代码:

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

1 个答案:

答案 0 :(得分:3)

正如Fyodor所指出的那样,不是函数创建使编译器生成隐藏类。隐藏的类用于实现部分应用程序。

在F#中,部分应用程序和lambda被实现为编译器生成的类,该类扩展了抽象类。 C#lambda依赖于委托。由于JVM没有委托,因此IIRC Java和Scala使用与F#类似的技术。

我怀疑F#编译器会为每个部分应用程序生成一个类,因为它比收集所有部分应用程序并合并相同的应用程序更简单。

它还有助于F#程序的可调试性,因为该名称暗示了部分应用程序的完成位置:main@31-1 =>在第31行的main函数中。此名称(如果包含在日志或性能运行中)可以帮助识别什么内容。部分应用会引起问题。

这是以增加P#汇编文件的大小为代价的,如Pavel的评论所述。