此类型注释如何工作,为什么另一个注释不起作用? [英] How this type annotation works, and why the other one does not?

查看:63
本文介绍了此类型注释如何工作,为什么另一个注释不起作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请解释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)) =
    ...

谢谢.

推荐答案

这种看起来像巫毒教徒的语法称为"静态解析的类型参数".想法是要求编译器检查作为泛型参数传递的类型是否具有某些成员(在您的示例中为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,这意味着每次与新的泛型参数一起使用时,都必须对其进行复制"(此技术有时也称为"单态化),这就是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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆