在 F# 中,inline
关键字似乎与我以前在 C 等语言中使用的有些不同。例如,它似乎会影响函数的类型(什么是“静态解析类型参数”?难道不是所有 F# 类型都是静态解析的吗?)
我应该在什么情况下使用 inline
函数呢?
inline
关键字表示函数定义应该插入到使用它的任何代码中。大多数情况下,这不会影响函数的类型。然而,在罕见的情况下,它可能会导致一个拥有更一般类型的函数,因为有些约束条件无法在.NET的编译形式中表达,但可以在内联函数时强制执行。
主要适用的情况是运算符的使用。
let add a b = a + b
将具有单态推断类型(可能是 int -> int -> int
,但如果您的代码在该类型中使用此函数,则可能是类似于 float -> float -> float
的类型)。然而,通过标记此函数为内联,F# 编译器将推断出多态类型:
let inline add a b = a + b
// add has type ^a -> ^b -> ^c when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
在.NET编程中,没有一种方式可以在编译代码时以一流的方式对此类型约束进行编码。但是,F#编译器可以在内联函数的位置强制执行此约束,以便所有运算符使用在编译时解析。
类型参数^a
、^b
和^c
是“静态解析类型参数”,这意味着参数的类型必须在使用这些参数的地方静态已知。这与常规类型参数(例如'a
、'b
等)形成对比,在常规类型参数中,参数的含义类似于“稍后将提供某个类型,但该类型可以是任何类型”。
let inline add a b = a + b
[<EntryPoint>]
let main args =
let one = 1
let two = 2
let three = add one two
// here add has been compiled to take 2 ints and return an int
let dog = "dog"
let cat = "cat"
let dogcat = add dog cat
// here add has been compiled to take 2 strings and return a string
printfn "%i" three
printfn "%s" dogcat
0
3
dogcat
let add a b = a + b
[<EntryPoint>]
let main args =
let one = 1
let two = 2
let three = add one two
// here add has been compiled to take 2 ints and return an int
let dog = "dog"
let cat = "cat"
let dogcat = add dog cat
// since add was not declared inline, it cannot be recompiled
// and so we now have a type mismatch here
printfn "%i" three
printfn "%s" dogcat
0
let dogcat = add dog cat
^^^ - This expression was expected to have type int
but instead has type string
let inline flip f x y = f y x
何时应该使用
inline
函数?
实际上,在实践中,inline
关键字最有价值的应用是将高阶函数内联到调用站点,其中它们的函数参数也被内联以产生单个完全优化的代码片段。
例如,以下fold
函数中的inline
使其快了5倍:
let inline fold f a (xs: _ []) =
let mutable a = a
for i=0 to xs.Length-1 do
a <- f a xs.[i]
a
请注意,这与大多数其他语言中的inline
几乎没有任何相似之处。您可以在C++中使用模板元编程实现类似的效果,但是F#也可以在编译后的程序集之间进行内联,因为inline
通过.NET元数据传递。
fold
在对复数进行操作时比内置的 Array.fold
快大约3倍。 - J Dinline
会有什么不利之处吗? - J Cooperinline
,但(与直觉相反)并不是因为它的内联执行! - J DF#组件设计指南中只提到了一点相关内容。 我的建议(与其中所说的相符)是:
inline
inline
。还有许多其他 "有趣" 的用途,可以使用内联和静态成员约束来执行类似于C++模板的"鸭子类型"场景。 我的建议是像瘟疫一样避免所有这些东西。
@kvb的答案更深入地介绍了什么是“静态类型约束”。
inline
关键字在数学库之外也非常有用。例如,在数据结构的上下文中,可以使用它将具体数据结构从抽象数据结构组合而成,而无需产生任何运行时性能损失。 - J D
p:'a
- 要求p
是类型'a
的后代,例如int
、obj
、'a when 'a :> SomeBaseType
等。另一方面,p:^a
- 要求在代码的调用点上直接支持某些特性或子类型的类型p
,或通过打开支持模块或访问扩展方法类等方式来支持。 - George