Poly 是一个小型库,旨在为当一个值具有一小组类型之一时,提供一个替代方案,而不是自己实现类型擦除。 Poly 库包含类型 Poly1
、Poly2
、Poly3
等,用于表示越来越大的可能类型池。 Poly2
与 Either
(一种常见的泛型函数式编程类型)同构。
要通过 Cocoapods 而不是 Swift Package Manager 在您的项目中使用此框架,请将以下依赖项添加到您的 Podfile。
pod 'Poly', :git => 'https://github.com/mattpolzin/Poly.git'
要为 Poly 创建 Xcode 项目,请运行 swift package generate-xcodeproj
用法将通过一个示例来解释。假设您有一些代码具有三种不同的类型:Dog
、Cat
和 Rat
。您还有一个协议 Animal
,它们都属于该协议。
如果您需要将所有三种类型的动物存储在一个地方(可能是一个数组),那看起来像这样:
let dog = Dog()
let cat = Cat()
let rat = Rat()
let animals: [Poly3<Dog, Cat, Rat>] = [
.init(dog),
.init(cat),
.init(rat)
]
要访问某种类型的所有动物,您可以使用下标,例如:
let dogs = animals[Dog.self]
let cats = animals[Cat.self]
let rats = animals[Rat.self]
您可以再次取出 Dog
、Cat
或 Rat
值,但您无法保证给定的 Poly
中存储的是哪种类型。
let animal = Poly3<Dog, Cat, Rat>(Dog())
let maybeDog: Dog? = animal.a
let maybeCat: Cat? = animal.b
let maybeRat: Rat? = animal.c
或者使用下标运算符使访问 Poly
的可能值之一更加直观:
let maybeDog2 = animal[Dog.self]
let maybeCat2 = animal[Cat.self]
let maybeRat2 = animal[Rat.self]
或者切换到可能的值:
switch animal {
case .a(let dog):
print(dog)
case .b(let cat):
print(cat)
case .c(let rat):
print(rat)
}
或者访问类型擦除的值:
let someAnimal: Any = animal.value
您可能考虑创建一个类型别名以简化您的生活:
typealias AnyAnimal = Poly3<Dog, Cat, Rat>
您也可能会觉得值得付出额外的努力,并为 Poly<Dog, Cat, Rat>
添加 Animal
一致性:
protocol Animal {
var speak: String { get }
}
extension Poly3: Animal where A == Dog, B == Cat, C == Rat {
var speak: String {
switch self {
case .a(let animal as Animal),
.b(let animal as Animal),
.c(let animal as Animal):
return animal.speak
}
}
}
所以现在你可以从上面的第一个例子中获取动物数组并:
let animalSounds = animals.map { $0.speak }
当每个被专门化的泛型都是 Encodable
/Decodable
时,所有 Poly
类型都是 Encodable
/Decodable
。 这是通过尝试编码或解码每种类型并使用第一次成功的尝试来实现的。 这意味着,只有当所有类型具有不同的编码/解码要求,或者至少类型以更严格的规则优先排序时,该行为才能按预期工作。
例如,给定以下类型:
struct Type1: Decodable {
let x: Poly2<Double, Int>
}
整数值永远不会被解码为 Int
,因为 Double
能够解码所有 Int
值。
您可以通过交换顺序为 Poly2<Int, Double>
来解决此问题。 现在,Poly
将尝试解码一个 Int
,并且只有在未找到 Int
时才尝试解码一个 Double
。