【勉強用】Heart of Swift - 2

今更ながら『Heart of Swift』を読んだので、勉強用としてまとめる。

https://heart-of-swift.github.io/

対外的にまとめるものではないです。

第1章 Value Semantics - [【勉強用】Heart of Swift - 1](https://qiita.com/1mash0/items/9a271fca20eb4cf323de)
第2章 Protocol-oriented Programming - この記事

# はじめに

Swiftは値型を中心としたプログラミング言語である。

Heart of Swift では Swift の中心となる概念を通して Swift という言語のコンセプトが書かれている。
中心となる概念は以下2つ。

- Value Semantics
- Protocol-oriented Programming

これらについては [WWDC2015](https://developer.apple.com/videos/wwdc2015/) のセッションの中で詳しく説明されている。

- ["Building Better Apps with Value Types in Swift"](https://developer.apple.com/videos/play/wwdc2015/414/)
- ["Protocol-Oriented Programming in Swift"](https://developer.apple.com/videos/play/wwdc2015/408/)

# Protocol-oriented Programming とは
WWDC2015のセッション[“Protocol-Oriented Programming in Swift”](https://developer.apple.com/videos/play/wwdc2015/408/)では、
- Self-requirement
- Protocol Extension
- プロトコルの後付け

など数多くの例が取り上げられているが、何が Protocol-oriented Programming なのかは明確に述べられていない。

筆者(koherさん)の解釈として、Protocol-oriented Programmingという用語は、Object-oriented Programming(オブジェクト指向プログラミング)との対比によって生み出されたものだと考える。
Swiftにおけるプロトコルは抽象化の道具。
オブジェクト指向プログラミングでは、
- クラスの継承
- ポリモーフィズム

を用いてコードを抽象化する。
しかしSwiftは値型中心の言語であり、原理的に継承することができない。
そのためSwiftではクラスの継承ではなくプロトコルが抽象化の主役となる。
Protocol-oriented Programmingとは、そのようなプロトコルを用いてコードを抽象化する手法全般を指している考える。

## プロトコルによる不適切な抽象化
以下のようなコードはSwiftにおいて必ずしも適切なプロトコルの使い方ではない。
```swift
protocol Animal {
    func foo() -> Int
}

struct Cat: Animal {
    func foo() -> Int { 2 }
}

struct Dog: Animal {
    func foo() -> Int { 1 }
}

let animal: Animal = Bool.random() ? Cat() : Dog()
print(animal.foo())
```

## Existential TypeとExistential Container
プロトコル型変数にインスタンスを格納する際には、Existential Containerという任意のサイズのインスタンスを格納できる入れ物が用いられる。
インスタンスはExistential Containerに入れられた上で変数に格納される。
```swift
protocol Animal {
    func foo() -> Int
}

struct Cat: Animal {
    var value: UInt8 = 2 // 1バイト
    func foo() -> Int { 2 }
}

struct Dog: Animal {
    var value: Int32 = 1 // 4バイト
    func foo() -> Int { 1 }
}

// 値型のためインスタンスが直接変数に格納される
// それぞれの変数は以下のバイト数の領域を必要とする
let cat: Cat = .init() // 1バイト
let dog: Dog = .init() // 4バイト

let animal: Animal = Bool.random() ? cat : dog
print(MemoryLayout.size(ofValue: animal) // 40バイト
```
`Animal`型変数に格納できるのは`Cat`と`Dog`のインスタンスだけではない。
`Animal`型として考えると適合した任意の型のインスタンスを格納できなくてはならず、`Animal`に適合した型は理論上いくらでも大きくできる(1バイトのStored Propertyを1000個持たせれば1000バイトになる)。

Existential Containerについての詳細は以下
[Swiftリポジトリ](https://github.com/apple/swift)にあるドキュメント["Type Layout"](https://github.com/apple/swift/blob/master/docs/ABI/TypeLayout.rst#existential-container-layout)の["Existential Container Layout"](https://github.com/apple/swift/blob/master/docs/ABI/TypeLayout.rst#existential-container-layout)を参照。

## 参照型とポリモーフィズム
Existential Containerのオーバーヘッドは値型とプロトコルに起因するものである。
全てクラスにした場合、参照型はインスタンスが存在するメモリのアドレスが変数に格納されるため8バイトで表される。
そのため代入する際Existential Containerに包む必要はない。
```swift
class Animal {}
class Cat: Animal {}
class Dog: Animal {}

let cat: Cat = .init() // 8バイト
let dog: Dog = .init() // 8バイト
let animal: Animal = Bool.random() ? cat : dog // 8バイト
```

## 値型に適したポリモーフィズム
### サブタイプポリモーフィズム
プロトコルによってサブタイプポリモーフィズムを実現する場合、プロトコルは型として用いられる。
以下のコードでは`Animal`プロトコルが`Animal`型として用いられる。
```swift
func useAnimal(_ animal: Animal) {
    print(animal.foo())
}
```

サブタイムポリモーフィズムの場合、`useAnimal`に引数を渡すときにインスタンスをExistential Containerに包む必要がある。また、`animal.foo()`を呼び出す箇所では`animal`が`Cat`か`Dog`か、また別のインスタンスかは実行時までわからない。

```swift
useAnimal(Cat())
useAnimal(Dog())
```

これら2つはどちらも同じように使うことができ、実行結果も同じ。
しかし、`Cat`の`foo`を呼び出すか、`Dog`の`foo`を呼び出すかは実行時に決定される(動的ディスパッチ)

### パラメトリックポリモーフィズム
以下のコードでは`Animal`プロトコルが型パラメータ`A`の制約として用いられる。
ジェネリック関数は様々な型に対して個別に関数を実装する代わりに、まとめて1つの関数として実装する手段と言える。
挙動としてはサブタイプポリモーフィズムとして実装した時と同じ。
```swift
func useAnimal<A: Animal>(_ animal: A) {
    print(animal.foo())
}
```

`Cat`や`Dog`などのそれぞれの型に対して関数をオーバーロードする代わりに、型パラメータという概念を導入して1つの関数として実装できるようにしたのがジェネリック関数。

実行時の挙動は、コンパイラが特殊化(最適化の一種)を行った場合、ジェネリック関数としての`useAnimal`のバイナリに加えて、型パラメータに`Cat`や`Dog`などの具体的な方を当てはめた`useAnimal`のバイナリも生成される。
そして`useAnimal`に`Cat`インスタンスを渡す箇所では`Cat`用の、`Dog`インスタンスを渡す箇所では`Dog`用の`useAnimal`が呼び出されるようコンパイルされる。
そのためExistential Containerに包む必要がなく、インスタンスが直接それぞれの`useAnimal`関数に渡される。
`animal.foo()`を呼び出す箇所についても、`Cat`用の`useAnimal`の中では`animal`が`Cat`であるとわかっているため、コンパイル時に`Cat`の`foo`を呼び出せばいいと決定できる(静的ディスパッチ)。
そのため実行時にメソッドを選択するオーバーヘッドが発生しない。

値型中心のSwiftにおいては、値型と組み合わせた時にオーバーヘッドの大きいサブタイプポリモーフィズムよりも、オーバーヘッドの発生しない**パラメトリックポリモーフィズム**の方が適している。

## 制約としてのプロトコル
- **プロトコルを「型として」ではなく「制約として」使用することを優先する**

[“Protocol-Oriented Programming in Swift”](https://developer.apple.com/videos/play/wwdc2015/408/)の中では特別そのことを強調されていないが、標準ライブラリにおけるプロトコルの利用例を見る限り、これこそがSwiftのプロトコルの使い方について最も重要なことだと考えられる。

# 「型として」・「制約として」のプロトコルの使い分け
```swift
// 型としてのプロトコル
func useAnimal(_ animal: Animal) {
    print(animal.foo())
}

// 制約としてのプロトコル
func useAnimal<A: Animal>(_ animal: A) {
    print(animal.foo())
}
```

## 型としてのプロトコルでしかできないこと
Heterogeneous Collection(異なる型のインスタンスが混在したコレクション)が必要な場合はプロトコルを型として使う必要がある。
制約としてのプロトコルの場合はHeterogeneousでない`Array`しか渡すことしかできない。
```swift
// 型としてのプロトコル
func useAnimals(_ animals: [Animal]) {
    ...
}
useAnimals([Cat(), Dog()])

// 制約としてのプロトコル
func useAnimals<A: Animal>(_ animals: [A]) {
    ...
}
useAnimals([Cat(), Dog()]) // コンパイルエラー
useAnimals([Cat(), Cat()]) // [Cat]を渡す(`A`はCat)
useAnimals([Dog(), Dog()]) // [Dog]を渡す(`A`はDog)
```

### 型パラメータ`A`に`Animal`を当てはめられない理由
プロトコル型がそのプロトコル自体に適合することをSelf-conformanceというが、一般的なSwiftのプロトコルはSelf-conformanceを持たない。
例として、`Animal`型は`Animal`プロトコル自体に適合しないため、`useAnimal`に`Animal`型の値を渡すことはできない。
```swift
func useAnimal<A: Animal>(_ animal: Animal) { ... }

let animal: Animal = Cat()
useAnimal(animal) // コンパイルエラー
```
ただし、例外的に[SE-0235](https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md)で`Error`プロトコルにSelf-conrmancceが追加された。
ref: https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md#adding-swifterror-self-conformance

## 制約としてのプロトコルでしかできないこと
代表的な例はSelf-requirement。`Equatable`プロトコルではSelf-requirementが使われている。
Self-requirementを持つプロトコルは制約として使うことだけが想定されている。
型として使うことに意味がないため、コンパイラが型として使うこと自体を禁止している。

```swift
protocol Equatable {
    static func == (
        lhs: Self, // Self-requirement
        rhs: Self // Self-requirement
    ) -> Bool
}

extension Int: Equatable {
    static func == (
        lhs: Int, // SelfがIntに置き換えられる
        rhs: Int // SelfがIntに置き換えられる
    ) -> Bool { ... }
}

extension String: Equatable {
    static func == (
        lhs: String, // SelfがStringに置き換えられる
        rhs: String // SelfがStringに置き換えられる
    ) -> Bool { ... }
}

// 制約としてのプロトコル
extension Sequence where Element: Equatable { // Equatable が制約として使われている
    func contains(_ element: Element) -> Bool {
        ...
    }
}
```
```swift
// 型としてのプロトコル
let a: Equatable = 42
let b: Equatable = "42"
a == b // Int と　String の比較はどこにも実装されていないためコンパイルエラー

let intA: Equatable = 42
let intB: Equatable = 42
intA == intB // Int 同士でも Equatable を介すとコンパイルエラー

let anyA: Any = 42
let anyB: Any = 42
anyA == anyB // Any 同士に == は実装されていないためコンパイルエラー
```

# プロトコルとリバースジェネリクス
様々なユースケースが生じる中で、制約としてのプロトコルによるコードの抽象化に欠けているものが明らかになってきた。
そのような問題と解決策についてまとめられたドキュメントが[ “Improving the UI of generics” ](https://forums.swift.org/t/improving-the-ui-of-generics/22814)。
このドキュメントの中で、リバースジェネリクスという新しい概念が説明され、その簡易系であるOpaque Result TypeがSwift5.1で部分的に導入された。

## 制約としてのプロトコルに欠けていた抽象化
Swiftの標準ライブラリでも制約としてのプロトコルが広く使われており、ほとんどすべてのプロトコルが制約として使用されている。
しかし、メソッドの戻り値の具体的な型を知る必要があるという問題がある。またもう一方で抽象的に書く場合Existential Containerのオーバーヘッドを受け入れる必要があるという問題もある。

```swift
protocol Sequence {
    associatedtype Iterator: IteratorProtocol // 制約として使われている
    func makeIterator() -> Iterator
}

extension Array: Sequence {
    // IteratorProtocl が IndexingIterator<[Element]>という具体的な型に置き換わる
    func makeIterator() -> IndexingIterator<[Element]> { ... }
}

extension String: Sequence {
    // IteratorProtocl が String.Iteratorという具体的な型に置き換わる
    func makeIterator() -> String.Iterator { ... }
}

let array = [1, 2, 3]
// iteratorの型はIndexingIterator<[Int]>
var iterator = array.makeIterator()
```
上記コードのように`IndexingIterator<[Int]>`が公開されているため、`IteratorProtocol`に適合する型を実装する際は、Swift標準ライブラリのIteratorProtocolに適合する大量のイテレータ型を意識する必要がある。
これ自体は問題ではないが、利用時にIndexingIterator<[Int]>の型を意識する必要ないのだが、型が公開されているため将来的にArrayに特化したより高速のイテレータが実装されとしてもmakeIteratorの戻り値の型を変更するのが困難になっている。
仮に以下のようにArrayのmakeIteratorが抽象化されていたら、Arrayに特化したより高速なイテレータに差し替えられてもAPIとしての表面上の型に変更はない。
```swift
extension Array: Sequence {
    func makeIterator() -> Elementを取り出す何らかのイテレータ { ... }
}
```
利用者にとって本来必要なのはこのレベルの抽象度であり、具体的なイテレータの型を知る必要はない。
しかしかといって戻り値の型にGeneralized Existentialを使う(IteratorProtocolを型として使う)とExistential Containerのオーバーヘッドの問題が残ってしまう。
※今のSwiftではGeneralized ExistentialとSelf-conformanceがサポートされていないため不可能
今望まれているのは、抽象的にコードを書きながら、具象型と同じパフォーマンスを出せること

## 抽象的なコードと具象型のパフォーマンス(戻り値の場合)
以下のような引数の型を抽象化する関数と、戻り値の型を抽象化する場合、利用者と実装者の関係が逆転する。
useAnimal: 利用者が具象型を決定し、実装者が抽象型を使用する。
makeAnimal: 実装者が具象型を決定し、利用者が抽象型を使用する。
```swift
func useAnimal<A: Animal>(_ animal: A) {
    print(animal.foo()) // 実装者が A を使用
}
useAnimal(Cat()) // 利用者が A を Cat に決定

func makeAnimal() -> A {
    Cat() // 実装者が A を Cat に決定
}
let animal = makeAnimal() // 利用者が A を使用
```
通常では戻り値の型を抽象化できないが、可能にするものとしてリバースジェネリクスという概念が提唱された。
ref: https://forums.swift.org/t/reverse-generics-and-opaque-result-types/21608
doc: “Improving the UI of generics”

Swift5.1時点ではリバースジェネリクスは採択されていないが、サブセットと言える**Opaque Result Type**はSwift5.1で部分的にサポートされた。

仮に採択された場合は以下のように書くことができようになる。
```swift
func makeAnimal() -> <A: Animal> A {
    Cat()
}

let animal = makeAnimal()
print(animal.foo())

let cat: Cat = animal // コンパイル時には A と　Cat は明確に区別されるためコンパイルエラー
```
```swift
extension Array: Sequence {
    func makeIterator() -> <I: IteratorProtocol> I where I.Element == Element { ... }
}

// makeIterator メソッドにリバースジェネリクスが使われたとき
var iterator: IndexingIterator<[Int]> = [2, 3, 5].makeIterator() // コンパイルエラー

// 以下のような書き方を強制する
var iterator = [2, 3, 5].makeIterator()
```

## Opaque Result Type
Opaque Result Typeはリバースジェネリクスを簡潔に書くためのシンタックスシュガーだと考えることができる。
SE-0244で部分的に採択され、Swift5.1でサポートされた
ref: https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md

以下のコードは全く同じことを意味する。
```swift
// リバースジェネリクス
func makeAnimal() -> <A: Animal> A {
    Cat()
}

// Opaque Result Type
func makeAnimal() -> some Animal {
    Cat()
}
```

## Opaque Argument Type
Opaque Result Typeと同じように、ジェネリックな引数をsomeを使って簡潔に書けるようにしようというものがOpaque Argument Type。

以下のコードは全く同じ意味になる。
```swift
// ジェネリクス
func useAnimal<A: Animal>(_ animal: A) {
    print(animal.foo())
}

// Opaque Argument Type
func useAnimal(_ animal: some Animal) {
    print(animal.foo())
}
```

## ジェネリクスでしかできないこと
Opaque Result TypeとOpaque Argument Typeを合わせてOpaque Typeと呼ぶ。
Opaque Typeがあればジェネリクスが不要になる、ということはなくジェネリクスにしかできないことがある。
同種の`Animal`を2つ引数に取りたい場合にはジェネリクスを使うしかないため、Opaque Typeはジェネリクスでできることの一部を簡潔に書くための手段でしかない。
```swift
// ジェネリクス
func useAnimalPair<A: Animal>(_ pair: (A, A)) {
    ...
}

// Opaque Type
func useAnimalPair(_ pair: (some Animal, some Animal)) {
    ...
}
// ↓と同じ意味になってしまう
func useAnimalPair<A1: Animal, A2: Animal>(_ pair: (A1, A2)) { // これはダメ
    ...
}
```

## Opaque TypeとExistential Type
Opaque Typeに`some`というキーワードが必要なように、Existential Typeにも`any`というキーワードを付与する。
```swift
func useAnimal(_ animal: any Animal) { // any があるので「型として」
    print(animal.foo())
}

func useAnimal<A: Animal>(_ animal: A) { // any がないので「制約として」
    print(animal.foo())
}
```
また、`any`というキーワードは`some`と対比した時に複数の`Animal`を考えると、
`[some Animal]`: ある(`some`) `Animal`の`Array`
`[any Animal]`: 任意の(`any`) `Animal`の`Array`
となり、言語的に自然である。

```swift
func useAnyAnimals(_ animals: [any Animal]) {
    animals.forEach { print($0.foo()) }
}

useAnyAnimals([Cat(), Dog()])
useAnyAnimals([Cat(), Cat()])

func useSomeAnimals(_ animals: [some Animal]) {
    animals.forEach { print($0.foo()) }
}

useSomeAnimals([Cat(), Dog()]) // コンパイルエラー
useSomeAnimals([Cat(), Cat()])
```

## プロトコルとリバースジェネリクスのまとめ
|  | 引数 | 戻り値 |
| -- | -- | -- |
| **ジェネリクス**<br>制約としてのプロトコル | `<A: Animal>(A) -> Void` | `() -> <A: Animal> A` |
| **Opaque Type**<br>ジェネリクスのシュガー | `(some Animal) -> Void` | `() -> some Animal` |
| **Existential Type**<br>型としてのプロトコル | `(any Animal) -> Void` | `() -> any Animal` |

ジェネリクスとOpaque Typeはコンパイル時に静的に抽象化を行う。コード上で抽象化された型がコンパイル時に特殊化によって展開され、実行時のパフォーマンスに影響がない抽象化が可能になる。

Existential Typeは実行時に動的に抽象化を扱う。この時の主役は値で、値はプロトコルに適合していることだけが求められ、その型は重要視されない。値の挙動はExistential Containerを用いて動的に解決される。