我正在阅读Swift Evolution proposal 244 (Opaque Result Types),但不理解以下含义:
...存在类型...
一个人可以通过 使用 现有类型 形状而不是通用参数,但是 这样做将意味着更多的动态性和运行时开销 希望的。
答案 0 :(得分:0)
演进提案本身提供了一个存在类型的示例:
protocol Shape {
func draw(to: Surface)
}
使用protocol Shape
作为存在类型的示例看起来像
func collides(with: Shape) -> Bool
与使用通用参数Other
相对:
func collides<Other: Shape>(with: Other) -> Bool
在这里需要特别注意的是,协议Shape
本身不是存在类型,仅在"protocols-as-types"上下文中使用它,如上,从协议“创建”存在类型。参见this post from the member of Swift Core Team:
此外,协议目前还作为存在类型的拼写检查者,但这种关系一直是造成混淆的常见原因。
还引用Swift Generics Evolution文章(我建议阅读全文,这将对此进行更详细的解释):
区分协议类型和存在类型的最好方法是查看上下文。问自己:当我看到对诸如Shape之类的协议名称的引用时,它是在类型级别还是在值级别出现?回顾一些先前的示例,我们看到:
func addShape<T: Shape>() -> T // Here, Shape appears at the type level, and so is referencing the protocol type var shape: Shape = Rectangle() // Here, Shape appears at the value level, and so creates an existential type
为什么将它称为“存在的”?我从没看到过明确的确认,但我认为该功能是受具有更高级类型系统的语言启发的,例如考虑Haskell's existential types:
class Buffer -- declaration of type class `Buffer` follows here
data Worker x y = forall b. Buffer b => Worker {buffer :: b, input :: x, output :: y}
大致相当于这个Swift代码段(如果我们假设Swift的协议或多或少代表了Haskell的类型类):
protocol Buffer {}
struct Worker<X, Y> {
let buffer: Buffer
let input: X
let output: Y
}
请注意,Haskell在此处使用了forall
existential quantifier。您可以将其理解为“对于所有符合类型类的类型(在Swift中为“协议”)。类型Buffer
的{{1}}值将具有完全相同的类型,只要它们的Worker
和{ {1}}类型参数相同”。因此,给定
X
值Y
和extension String: Buffer {}
extension Data: Buffer {}
将具有完全相同的类型。
这是一项强大的功能,它允许诸如异类集合之类的事情,并且可以在需要“擦除”值的原始类型并将其“隐藏”在存在类型下的更多地方使用。像所有强大的功能一样,它可能会被滥用并使代码的类型安全性降低,因此应谨慎使用。
如果您想更深入地学习,请查看Protocols with Associated Types (PATs),由于各种原因,它们不支持存在性。还有一些Generalized Existentials提案在空中飘扬,但是从Swift 5.1开始,没有什么具体的提案。实际上,由OP链接的原始Opaque Result Types提案可以解决因使用PAT而引起的一些问题,并显着缓解Swift中缺乏通用的存在性。