.net 没有内联的静态类型约束?

.net 没有内联的静态类型约束?,.net,compiler-construction,f#,language-design,.net,Compiler Construction,F#,Language Design,最近有几个关于静态类型约束和内联的问题: 我特别注意到两种说法: 在.NET上的编译代码中,无法以第一类的方式对该类型约束进行编码。但是,F编译器可以在其内联函数的位置强制执行此约束,以便在编译时解析所有运算符的使用 …但是F也可以在编译的程序集之间内联,因为内联是通过.NET元数据传递的 后者似乎与前者相矛盾。我想知道为什么内联需要与静态类型约束特性相关。F编译器不能按照我认为C++模板和.NET泛型的方式工作,并在编译时根据需要生成特定类型的、可重用的函数吗? 例如: 多态定义: let a

最近有几个关于静态类型约束和内联的问题:

我特别注意到两种说法:

在.NET上的编译代码中,无法以第一类的方式对该类型约束进行编码。但是,F编译器可以在其内联函数的位置强制执行此约束,以便在编译时解析所有运算符的使用

…但是F也可以在编译的程序集之间内联,因为内联是通过.NET元数据传递的

后者似乎与前者相矛盾。我想知道为什么内联需要与静态类型约束特性相关。F编译器不能按照我认为C++模板和.NET泛型的方式工作,并在编译时根据需要生成特定类型的、可重用的函数吗? 例如:

多态定义:

let add (lhs:^a) (rhs:^a) = lhs + rhs
用法:

let a = add 2 3
let b = add 2. 3.
let b = add 4. 5.
编译器生成的添加函数:

let add_int (lhs:int) (rhs:int) = lhs + rhs
let add_float (lhs:float) (rhs:float) = lhs + rhs
编译器重写用法:

let a = add_int 2 3
let b = add_float 2. 3.
let c = add_float 4. 5.

我既不是编译器作者,也不是语言设计师,尽管这些主题确实让我很感兴趣,所以请指出我的天真。

我认为在技术上允许静态成员约束而不使用内联。从历史上看,我认为该功能主要用于标准的数学运算符,但只有在最近的版本中,它才变得更强大、更有用

但是,有几个很好的理由可以解释为什么您不想经常使用它:

类型签名的可读性如果所有泛型方法都被解释为具有hat类型的方法,那么类型签名的可读性就会受到影响,因为每个类型参数都会有大量需要实现的方法

互操作性声明为内联的函数/方法不能由其他.NET语言调用。由于F非常关注互操作性,所以选择可以从C中使用的常规泛型方法似乎是合理的

按照您的建议和C++/CLI编译器的方式生成专门的函数将是F实现内联的另一种替代方法,但可能需要更多的工作。在任何情况下,您仍然需要以某种方式标记函数,因此在F中编写泛型函数的自然方式是使用标准泛型


最后,解释两种说法之间的矛盾。我相信内联是通过.NET元数据传递的,这实际上意味着有关内联的信息以某种二进制格式存储在.NET资源中。这意味着它是.NET程序集的一部分,但只有F才能理解它。

我认为在技术上允许静态成员约束而不使用内联。从历史上看,我认为该功能主要用于标准的数学运算符,但只有在最近的版本中,它才变得更强大、更有用

但是,有几个很好的理由可以解释为什么您不想经常使用它:

类型签名的可读性如果所有泛型方法都被解释为具有hat类型的方法,那么类型签名的可读性就会受到影响,因为每个类型参数都会有大量需要实现的方法

互操作性声明为内联的函数/方法不能由其他.NET语言调用。由于F非常关注互操作性,所以选择可以从C中使用的常规泛型方法似乎是合理的

按照您的建议和C++/CLI编译器的方式生成专门的函数将是F实现内联的另一种替代方法,但可能需要更多的工作。在任何情况下,您仍然需要以某种方式标记函数,因此在F中编写泛型函数的自然方式是使用标准泛型

最后,解释两种说法之间的矛盾。我相信内联是通过.NET元数据传递的,这实际上意味着有关内联的信息以某种二进制格式存储在.NET资源中。这意味着它是.NET程序集的一部分,但只有F才能理解它。

两位

首先,事情就不能这样吗?对单独编译通常被视为一个布尔值,但它实际上是一个连续体。通常,静态类型语言的单独编译意味着,我只需要知道类/方法的接口/签名类型,就可以针对另一个模块/程序集中的实体进行编译,并成功生成好代码,或者生成有关错误的有用诊断。关键思想是类型签名是对基本约束的简洁总结

你可以把它按比例移动到频谱的另一端,并做一个类似于F内插或C++模板的鸭式输入,基本上,如果你将这些数据类型插入到该方法的代码体中,它会编译吗?这是可以做到的,但它的编译和测试速度通常较慢 在出现故障时提供较差的诊断。这些后果是由于缺乏简洁的摘要造成的,而这正是独立编译的典型产物

从评估实用性/可用性的一个完全不同的轴来看,内联/C++模板模型非常灵活和复杂,而典型类型系统的东西不太灵活,但通常足以表达大多数抽象和非常简单

无论如何,任何事情都是可能的,但是如果您做的事情比为类型/泛型/类型类设计的一些标准机制更灵活,那么在编译速度和错误诊断质量方面,您往往会跌入悬崖。因此,在F中,只有当您通过内联方式明确请求时,该机制才会生效

第二,当.NET元数据无法表示这些约束时,F如何跨程序集内联?简单地说,具有任何内联内容的F程序集除了通常的.NET元数据(基本上表示F代码体)之外,还包含额外的F特定元数据,因此它可以在引用程序集的调用站点内联。特定于F的元数据作为资源添加到.NET程序集中。在F PowerPack中,有一个元数据读取器模块,使您能够“反映”一些额外的F元数据。在FSharp.Core的情况下,这些额外的元数据被分解成FSharp.Core.sigdata和FSharp.Core.optdata文件,而不是嵌入到FSharp.Core.dll运行时程序集中。这使得F运行时更小,因为在设计时只需要sigdata/optdata。

两位

首先,事情就不能这样吗?对单独编译通常被视为一个布尔值,但它实际上是一个连续体。通常,静态类型语言的单独编译意味着,我只需要知道类/方法的接口/签名类型,就可以针对另一个模块/程序集中的实体进行编译,并成功生成好代码,或者生成有关错误的有用诊断。关键思想是类型签名是对基本约束的简洁总结

你可以把它按比例移动到频谱的另一端,并做一个类似于F内插或C++模板的鸭式输入,基本上,如果你将这些数据类型插入到该方法的代码体中,它会编译吗?这是可以做到的,但它的编译速度通常较慢,并且在出现故障时提供较差的诊断。这些后果是由于缺乏简洁的摘要造成的,而这正是独立编译的典型产物

从评估实用性/可用性的一个完全不同的轴来看,内联/C++模板模型非常灵活和复杂,而典型类型系统的东西不太灵活,但通常足以表达大多数抽象和非常简单

无论如何,任何事情都是可能的,但是如果您做的事情比为类型/泛型/类型类设计的一些标准机制更灵活,那么在编译速度和错误诊断质量方面,您往往会跌入悬崖。因此,在F中,只有当您通过内联方式明确请求时,该机制才会生效


第二,当.NET元数据无法表示这些约束时,F如何跨程序集内联?简单地说,具有任何内联内容的F程序集除了通常的.NET元数据(基本上表示F代码体)之外,还包含额外的F特定元数据,因此它可以在引用程序集的调用站点内联。特定于F的元数据作为资源添加到.NET程序集中。在F PowerPack中,有一个元数据读取器模块,使您能够“反映”一些额外的F元数据。在FSharp.Core的情况下,这些额外的元数据被分解成FSharp.Core.sigdata和FSharp.Core.optdata文件,而不是嵌入到FSharp.Core.dll运行时程序集中。这使得F运行时更小,因为在设计时只需要SigDAT/OPTDATA。

值得注意的是,C++模板和.NET泛型的编译模型完全不同,但比较它们不是我今天要写的一篇文章。也许有人可以在网上找到一个很好的比较,这个比较有足够的深度,可以看到取舍。完全不同吗?:-你想说什么?布莱恩?他们不一样,从长远来看,我相信,这是我试图表达的要点。@布赖恩,很抱歉,这是我在幽默方面的一次微弱尝试。我希望我没有冒犯。值得一提的是,C++模板和.NET泛型的编译模型是完全不同的,但是比较它们并不是我今天要写的文章。也许有人可以在网上找到一个很好的比较,这个比较有足够的深度,可以看到取舍。完全不同吗?:-你想说什么@Brian?他们不是s
“我相信,ame,从长远来看,是我试图表达的要点。”“布莱恩,很抱歉,这只是我在幽默方面的一次微弱尝试。我希望我没有冒犯你。