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

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

问题描述

请解释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屋!

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