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

Ste*_*sen 1 .net compiler-construction f# language-design

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

  1. 在F#中使用`inline`
  2. F#如何编译可以将多种不同参数类型带入IL的函数?

特别是两个陈述让我印象深刻:

  1. "有没有办法来编码在.NET编译代码一流的方式这种类型的限制.然而,F#编译器可以在它内联函数,使所有运营商使用在编译时解析的现场强制执行这一约束".

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

后者几乎似乎与前者相矛盾.我想知道为什么内联需要与静态类型约束功能相关.不能在F#编译器的工作方式(我相信)C++模板和泛型.NET做,并根据需要在编译时动态生成特定的类型,可重复使用的功能呢?

例如:

多态定义:

let add (lhs:^a) (rhs:^a) = lhs + rhs
Run Code Online (Sandbox Code Playgroud)

用法:

let a = add 2 3
let b = add 2. 3.
let b = add 4. 5.
Run Code Online (Sandbox Code Playgroud)

编译器生成的添加功能:

let add_int (lhs:int) (rhs:int) = lhs + rhs
let add_float (lhs:float) (rhs:float) = lhs + rhs
Run Code Online (Sandbox Code Playgroud)

编译器重写用法:

let a = add_int 2 3
let b = add_float 2. 3.
let c = add_float 4. 5.
Run Code Online (Sandbox Code Playgroud)

我不是编译器作家也不是语言设计师,虽然这些主题对我很感兴趣,所以请指出我的天真.

Bri*_*ian 12

两位.

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

您可以将其按比例滑动到光谱的另一端,并执行F#内联或C++模板执行的类似鸭子的操作,其中基本上"如果您将这些数据类型插入到此方法的代码主体中,会编译吗?" 这是可行的,但它通常会因为编译速度慢而在事情失败时提供较差的诊断.这些后果源于缺乏"简洁摘要",这是"单独编译"中"独立"的典型工具.

(从评估效用/可用性的完全不同的轴来看,内联/ C++模板模型"极其灵活"和"极其复杂",而典型的类型系统内容"灵活性较差但通常足以表达大多数抽象", "非常简单".)

无论如何,所以一切皆有可能,但如果你做的事情比一些标准的易于理解的类型/泛型/类型类机制更"灵活",那么在编译速度和错误诊断质量方面,你往往会陷入困境. .因此,在F#中,只有当您通过具体请求时,该机制才会启动inline.

其次,当.NET元数据无法表达这些约束时,F#如何管理"内联组件"?简单来说,带有任何inline东西的F#程序集包含额外的F#特定元数据(除了通常的.NET元数据之外),它基本上代表"F#代码体",因此它可以在引用程序集的调用站点处内联.F#特定的元数据嵌入.NET程序集中的"资源".在F#PowerPack中,有一个元数据读取器模块,使您能够"反映"一些额外的F#元数据.(在FSharp.Core的情况下,这个额外的元数据被分解为FSharp.Core.sigdata和FSharp.Core.optdata文件,而不是嵌入到FSharp.Core.dll运行时程序集中.这使F#运行时更小,


Tom*_*cek 5

我认为在没有内联的情况下允许静态成员约束在技术上是可行的.从历史上看,我认为该功能主要用于标准数学运算符,但它已经变得更强大,仅在最近的版本中更有用.

但是,有几个很好的理由可能导致您不想经常使用它:

  • 类型签名的可读性如果所有泛型方法都被解释为具有"帽子类型"的方法,则类型签名的可读性将受到影响,因为每个类型参数将具有需要实现的大量方法列表.

  • 互操作性声明为inline其他.NET语言无法调用的函数/方法.由于互操作性是F#非常关注的问题,因此选择通常的通用方法(可以从C#中使用)似乎是合理的.

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

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