swift - Swift 中的“存在类型”是什么意思?
问题描述
我正在阅读Swift Evolution 提案 244(不透明的结果类型),但不明白以下内容是什么意思:
... 存在型 ...
可以通过使用存在类型Shape 而不是泛型参数来组合这些转换,但这样做会意味着比预期更多的动态性和运行时开销。
解决方案
进化提案本身给出了一个存在类型的例子:
protocol Shape {
func draw(to: Surface)
}
用作存在类型的示例protocol Shape
如下所示
func collides(with: Shape) -> Bool
而不是使用通用参数Other
:
func collides<Other: Shape>(with: Other) -> Bool
需要注意的是,该Shape
协议本身并不是一个存在类型,仅在上面的“协议作为类型”上下文中使用它会从中“创建”一个存在类型。请参阅Swift Core 团队成员的这篇文章:
此外,协议目前作为存在类型的拼写具有双重职责,但这种关系一直是混淆的常见来源。
另外,引用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 的存在类型:
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
量词。您可以将其理解为“对于符合Buffer
类型类(Swift 中的“协议”)的所有Worker
类型,只要它们的类型参数X
和Y
类型参数相同,类型的值就会具有完全相同的类型”。因此,给定
extension String: Buffer {}
extension Data: Buffer {}
值Worker(buffer: "", input: 5, output: "five")
并且Worker(buffer: Data(), input: 5, output: "five")
将具有完全相同的类型。
这是一个强大的功能,它允许诸如异构集合之类的事情,并且可以在更多需要“擦除”值的原始类型并将其“隐藏”在存在类型下的地方使用。像所有强大的功能一样,它可能会被滥用,并且会使代码的类型安全性降低,因此应谨慎使用。
如果您想更深入地了解,请查看具有关联类型 (PAT) 的协议,由于各种原因,目前不能将其用作存在。也有一些广义存在主义提案或多或少地定期提出,但截至 Swift 5.3 还没有具体的提案。事实上,由 OP 链接的原始Opaque Result Types提案可以解决使用 PAT 引起的一些问题,并显着缓解 Swift 中缺乏泛化存在的问题。
推荐阅读
- ffmpeg - 使用录制 rtsp 流时间歇性视频挂断
- r - 在闪亮的应用程序中使用 actionButton 更新数据框的值
- hl7-fhir - “为所有资源定义的通用参数”的 Hapi Fhir 常量
- postgresql - 无法链接到正在运行的 postgres 数据库 docker 容器并运行 DDL 命令
- reporting-services - Microsoft SQL Server Report Builder 创建数据集以存储中文名称
- python - 设置 QMainWindow 的样式表覆盖 QWidget
- typescript - 如何定义类型,以便我能够将具有通用字符串键的记录分配给自定义枚举键
- active-directory - 使用简单绑定将 LDAPS 负载平衡到 Active Directory
- android-studio - 在 linarLayout 的中心创建 textViews
- javascript - 是否可以通过 React 编写的站点中的元素获取事件处理程序?