这种类型的注释是如何工作的,为什么另一种注释没有?

End*_*rju 7 f#

请解释drawShape功能背后的魔力.1)为什么它可以工作 - 我的意思是它如何称呼Draw成员,2)它为什么需要inline

type Triangle() =
    member x.Draw() = printfn "Drawing triangle"

type Rectangle() =
    member x.Draw() = printfn "Drawing rectangle"

let inline drawShape (shape : ^a) =
    (^a : (member Draw : unit->unit) shape)

let triangle = Triangle()
let rect = Rectangle()

drawShape triangle
drawShape rect
Run Code Online (Sandbox Code Playgroud)

接下来的问题是 - 是否可以drawShape使用如下所示的参数类型注释来编写函数?我发现它与第一个签名完全相同,但我无法完成身体.

let inline drawShape2 (shape : ^a when ^a : (member Draw : unit->unit)) =
    ...
Run Code Online (Sandbox Code Playgroud)

提前致谢.

Fyo*_*kin 13

这种看起来像Voodoo的语法称为" 静态解析类型参数 ".我们的想法是要求编译器检查作为泛型参数传递的类型是否具有某些成员(在您的示例中Draw).

由于CLR不支持这样的检查,它们必须在编译时完成,F#编译器很乐意为你做,但它还有一个代价:因为没有CLR支持,所以没有办法编译这样的IL的函数,这意味着它必须在每次与新的泛型参数一起使用时被"复制"(这种技术有时也被称为" 单态化 "),这就是inline关键字的用途.

至于调用语法:由于某种原因,只是声明参数本身的约束不会削减它.每次实际引用该成员时都需要声明它:

// Error: "x" is unknown
let inline f (a: ^a when ^a: (member x: unit -> string)) = a.x() 

// Compiles fine
let inline f a = (^a: (member x: unit -> string)( a )) 

// Have to jump through the same hoop for every call
let inline f (a: ^a) (b: ^a) = 
  let x = (^a: (member x: unit -> string)( a ))
  let y = (^a: (member x: unit -> string)( b ))
  x+y

// But can wrap it up if it becomes too messy
let inline f (a: ^a) (b: ^a) = 
  let callX t = (^a: (member x: unit -> string) t)
  (callX a) + (callX b)

// This constraint also implicitly carries over to anybody calling your function:
> let inline g x y = (f x y) + (f y x)
val inline g : x: ^a -> y: ^a -> string when  ^a : (member x :  ^a -> string)

// But only if those functions are also inline:
> let g x y = (f x y) + (f y x)
Script.fsx(49,14): error FS0332: Could not resolve the ambiguity inherent in the use of the operator 'x' at or near this program point. Consider using type annotations to resolve the ambiguity.
Run Code Online (Sandbox Code Playgroud)