这种类型注释是如何工作的,为什么另一个不能? [英] How this type annotation works, and why the other one does not?
问题描述
请解释drawShape
函数背后的魔力.1) 为什么它有效——我的意思是它如何调用 Draw
成员,2) 为什么它需要 inline
?
Please explain the magic behind drawShape
function. 1) Why it works at all -- I mean how it calls the Draw
member, 2) why it needs to be 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
下一个问题是——是否可以使用如下所示的参数类型注释来编写 drawShape
函数?我发现它和第一个签名完全一样,但我无法完成正文.
And the next issue is -- is it possible to write drawShape
function using parameter type annotation like below? I found that it has exactly the same signature as the first one, but I'm unable to complete the body.
let inline drawShape2 (shape : ^a when ^a : (member Draw : unit->unit)) =
...
提前致谢.
推荐答案
这种类似于 Voodoo 的语法称为静态解析类型参数".这个想法是要求编译器检查作为泛型参数传递的类型是否具有某些成员(在您的示例中 - Draw
).
This Voodoo-looking syntax is called "statically resolved type parameter". The idea is to ask the compiler to check that the type passed as generic argument has certain members on it (in your example - Draw
).
由于 CLR 不支持此类检查,因此它们必须在编译时完成,F# 编译器很乐意为您完成,但这也有代价:因为没有 CLR 支持,所以没有办法要将此类函数编译为 IL,这意味着每次与新的通用参数一起使用时都必须复制"它(此技术有时也称为monomorphisation"),这就是 inline
关键字的用途.
Since CLR does not support such checks, they have to be done at compile time, which the F# compiler is happy to do for you, but it also comes with a price: because there is no CLR support, there is no way to compile such function to IL, which means that it has to be "duplicated" every time it's used with a new generic argument (this technique is also sometimes known as "monomorphisation"), and that's what the inline
keyword is for.
至于调用语法:出于某种原因,仅声明对参数本身的约束并不能削减它.每次实际引用成员时都需要声明它:
As for the calling syntax: for some reason, just declaring the constraint on the parameter itself doesn't cut it. You need to declare it every time you actually reference the member:
// 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.
这篇关于这种类型注释是如何工作的,为什么另一个不能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!