前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swift 5.7 针对主要关联类型的轻量级同类型优化

Swift 5.7 针对主要关联类型的轻量级同类型优化

原创
作者头像
DerekYuYi
修改2022-10-08 18:17:38
5470
修改2022-10-08 18:17:38
举报
文章被收录于专栏:Swift-开源分析

介绍

本篇提议引入一种新的语法,用来遵守泛型参数并通过同一类型约束关联类型。

目的

先来看一个例子,该例中函数是用来返回源文件中的多个行数。

代码语言:Swift
复制
struct LineSequence : Sequence {
  struct Iterator : IteratorProtocol {
    mutating func next() -> String? { ... }
  }
  
  func makeIterator() -> Iterator {
    return Iterator()
  }
}

func readLines(_ file: String) -> LineSequence { ... }

如果你正在实现开发一个语法高亮库,并且定义一个函数readSyntaxHighlightedLines读取高亮语法的行数, 这个函数使用SyntaxTokenSequence来包装返回结果,结果中的元素类型是[Token], 代表每一行中语法高亮的 token 数组。例如:

代码语言:Swift
复制
func readSyntaxHighlightedLines(_ file: String)
  -> SyntaxTokenSequence<LineSequence> {
  ...
}

注意:这里隐藏了一个等价关系:Token相当于序列的元素,是序列的关联类型,[Token]与Sequence是等价的。这与我们平时使用关联类型用法也是一样的。

这个函数的返回结果是不是很复杂?借鉴前面提议实现的特性some, 貌似这里我们使用它来隐藏背后的实际类型,例如:

代码语言:Swift
复制
func readSyntaxHighlightedLines(_ file: String)
  -> some Sequence {
  ...
}

但是, 函数readSyntaxHighlightedLines()已经无法充分表达原有的意思,因为后者无法表达:结果Sequence的关联类型Element[Token]中的元素是等价的。

再来看另外一个例子,这个例子中的函数作用是操作两个String数组:

代码语言:Swift
复制
func concatenate(_ lhs: Array<String>, _ rhs: Array<String>) -> Array<String> {
  ...
}

函数concatenate()中的参数类型和返回类型都是Array<String>, 我们可以把它概括为抽象的序列实现,比如使用泛型来隐藏具体类型,并通过条件语句来限制泛型类型。比如这样用:

代码语言:Swift
复制
func concatenate<S: Sequence>(_ lhs: S, _ rhs: S) -> S where S.Element == String {
...
}

这样写有什么问题?在读写声明时都会带来开销,而且这与前面简单使用Array<String>来实现函数声明完全不同。对于只有一个相同类型的情况,最好有一个更简单的方案来处理。下面看看本提议如何解决。

提议的解决方案

本提议会提出一种新的语法,用于声明协议一致性需求,以及协议主要关联类型上的一个或者多个相同类型的需求。这种新语法看起来像是将具体泛型类型应用到类型参数列表,并允许你编写Sequence<String>或者Sequence<[Token]>这种形式。这种形式类似于Array<String>Array<[Token]>

协议可以使用新语法来声明多个主要关联类型,这个新语法与具体类型的泛型参数列表表达相似,例如:

代码语言:Swift
复制
protocol Sequence<Element> {
  associatetype Element
  associatetyoe Iterator : IteratorProtocol
    where Element == Iterator.Element
    ...
}

protocol dictionaryProtocol<Key, Value> {
  associatedtype Key: Hashable
  associatedtype Value
  ...
}

在尖括号的类型参数列表中,可以声明带有主要关联类型的协议。例如,不透明结果类型现在可以约束主要关联类型, 上述readSyntaxHighlightedLines()函数可以这样表达:

代码语言:Swift
复制
func readSyntaxHighlightedLines(_ file: String) -> some Sequence<[Token]> {
  ...
}

上述concatenate()函数现在可以这样写:

代码语言:Swift
复制
func concatenate<S: Sequence<String>>(_ lhs: S, _ rhs: S) -> S {
  ...
}

主要关联类型旨在用于调用方提供的关联类型。这些关联类型通常由遵循类型的泛型表达。例如,ElementSequence的主要关联类型,因为Array<Element>Set<Element>都遵循Sequence协议,Element则是由它们对应具体类型的泛型参数来表示。比如Set<Int>的泛型参数类型此时是Int, 则Element此时是与Int类型对应。

具体设计细节

在协议声明中,协议名称后面可以有一个主要关联类型列表,关联类型声明在尖括号'<>'里。

这个关联列表是可选的,你可以写也可以像之前声明协议样,后面不用跟任何声明。

如果关联列表存在,必须至少有一个主要关联类型存在。多个关联类型在'<>'中以逗号隔开。

关联类型列表中的每个关联类型必须要定义在对应的协议声明内,或者继承的协议声明内。

所以原来协议定义语法需要加上'主要关联类型列表',修改如下:

  • 协议声明 -> attributes(可选) 访问修饰符(可选) protocol 协议名 主要关联类型列表 协议继承语句 泛型条件语句 协议体
  • 主要关联类型列表 -> < 主要关联类型条目 >
  • 主要关联类型条目 -> 主要关联类型 | 主要关联类型 , 主要关联类型条目
  • 主要关联类型 -> 类型名称 看两个例子:
代码语言:Swift
复制
// 关联类型`Element`在协议体内声明
protocol setProtocol<Element> {
    associatedType Element: Hashable
}

protocol SortedMap {
    associatedType Key
    associatedType Value
}

// 主要关联类型`key`和`Value`在继承的协议`SortedMap`中声明
protocol PersistentSortedMap<Key, Value> : SortedMap {
    ...
}

在使用协议时,可以使用一个或者多个类型参数来约束协议,比如P<Arg1, Arg2...>. 这些参数可以省略,这样该协议就不受约束。如果指定了类型参数,则类型参数的数量不能少于或者多于主关联类型的数量,否则会报错。

向协议添加主关联类型可以兼容源代码,该协议仍然可以在没有<>的情况下使用,就跟没有主关联类型的情况一样。

约束协议表达

约束协议语法可以出现在函数声明的多个地方,比如下面的各种协议约束语法都等同于 where 语句语法表达。下面举例说明:

  • 第一种情况是 extension 的扩展类型,例子中的 <String> 等价于 where Element == String 语法,例如:
代码语言:Swift
复制
extension Collection<String> { ... }

// 等价于
extension Collection where Element == String { ... }
  • 第二种情况是继承协议,也就是遵循某一个协议,例如:
代码语言:Swift
复制
protocol TextBuffer: Collection<String> { ... }

// 等价于

protocol TextBuffer Collection where Element == String  { ... }
  • 第三种情况是继承泛型,也就是需要指定元素类型为某个类型,比如集合的指定元素为 String类型:
代码语言:Swift
复制
  func sortLines<S : Collection<String>>(\_ lines: S) -> S

  // 等价于:

  func sortLines<S : Collection>(\_ lines: S) -> S

    where S.Element == String
  • 第四种情况是继承关联类型,比如:
代码语言:Swift
复制
protocol Document {

   associatedtype Lines: Collection<String>

}

// 等价于:

protocol Document {

  associatedtype Lines: Collection where Lines.Element == String

}
  • 第五种情况是在where子句的右侧,例如:
代码语言:Swift
复制
func merge<S : Sequence>(\_ sequences: S) where S.Element : Sequence<String>  

// 等价于:

func merge<S : Sequence>(\_ sequences: S)

where S.Element : Sequence, S.Element.Element == String
  • 第六种情况是不透明参数声明(some关键字声明),SE-0341 不透明参数声明在这篇提议中已经实现。举个例子说明:
代码语言:Swift
复制
func sortLines(\_ lines: some Collection:<String>)

// 等价于:

func sortLines<C: Collection<String>>(\_ lines: C)

// 也等价于:

func sortLines<C: Collection>(\_ lines: C) where C.Element == String
  • 第七种情况是协议参数包括嵌套的不透明参数. 例如:
代码语言:Swift
复制
func sort(elements: inout some Collection<some Equatable>) {}



// 等价于:

func sort<C: Collection, E: Equatable>(elements: inout C) where C.Element == E 

上述7种情况发生时,这种T: P<Arg1, Arg2...>语法糖表达都可以去糖化表达为 T: P,并在后面追加参数约束,

代码语言:Swift
复制
T :P

T.PrimaryType1 == Arg1

T.PrimaryType2 == Arg2

...

如果Arg1some参数,可以像上述第7种情况处理。

约束协议在不透明结果类型处表达

约束类型有可能在不透明结果类型处出现。比如:

代码语言:Swift
复制
func transformElements<S : Sequence<E>, E>(\_ lines: S) -> some Sequence<E>

使用some, 例如:

代码语言:Swift
复制
func transform(\_: some Sequence<some Equatable>) -> some Sequence<some Equatable>

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 目的
  • 提议的解决方案
  • 具体设计细节
    • 约束协议表达
      • 约束协议在不透明结果类型处表达
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档