F# inline是如何工作的?

14

据我了解,使用 F# 中的 inline 关键字可以在调用站点执行类型特化。也就是说:

val inline (+) : ^a -> ^b -> ^c
      when (^a or ^b) : (static member (+) : ^a * ^b -> ^c)

约束条件是 ^a 或者 ^b 必须有一个像 op_Addition 这样的静态成员或内置原语,可以用来填补这个空缺。

所以如果你有一个方法有一个 +,你传递一个 int 和一个 short 作为参数它就会展开 + 到一个指令来使用内置的 int 原语,如果你传递一个 float 和一个 byte 它就会使用 float 的原语加法操作码。

这个在编译时具体是如何完成的?你怎么能有一个在 CLR 中的方法可以根据类型切换使用哪种操作码或方法?

Reflection.Emit 是否支持此行为?我了解到内联是在调用站点执行的,这是否意味着代码不能在 C# 中工作?


我对内部机制并不是很了解,但看起来 F# 源代码定义 + 运算符的位置在 C:\Program Files (x86)\FSharp-2.0.0.0\source\fsharp\FSharp.Core\prim-types.fs 的第 3527 行,它会在编译时切换参数类型并直接发出 IL 代码。 - Juliet
1个回答

10
根据inline的建议,该代码在调用站点内联。在每个调用站点,您都知道具体的类型参数^T,因此将该类型的特定代码插入其中。
这是由F#编译器完成的,在其他上下文(如C#或Ref.Emit)中很难实现。
F#库有一些内联函数,仍然可以被其他语言调用,这些实现的运行时基于运行时类型进行动态分派,请参见F#核心库代码中prim-types.fs中的AdditionDynamic代码以了解其感觉。

那么你的意思是这基本上是一个复杂的编译器技巧?如果我从C#调用一个FSharpFunc<,>,我会得到期望的行为吗? - Michael B
2
类型 FSharpFunc 的任何实例(或大多数类型)都不是(也不能)inline的。 inline适用于方法和函数,而且在F#库中只有少量的方法和函数可以使用它。 大多数东西都可以从C#中正常调用,但一些F# inline函数不支持动态调用(比如sin),因为F#编译器必须在调用点处内联代码。 - Brian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接