SwiftのProtocol ExtensionはExistentialをopenする

この記事ではSwiftのProtocol ExtensionがExistentialをopenする事を説明します。

# Existentialと型変数の違い

SwiftのExistentialは、プロトコルに対して自動定義される型で、そのプロトコルを満たす型を代入できます。

```swift
protocol P {}

extension Int : P {}

var p: P = 3
```

ここで`p`の型は`P`のexistentialです。`P`のexistentialに代入できるのは、`P`を満たす型なので、つまり、ジェネリクスの`<T: P> T`で表される型変数です。`Int`は`<T: P> T`にマッチしますね。

ここで、`P`のexistentialと`<T: P> T`の違いを説明します。

`P`のexistentialは、条件を満たす型が**なんでも**代入できる型です。この型になった時点で、元の値の型の情報は消去されています。そのため、例えば下記のように異なる型を再代入する事ができます。

```swift
protocol P {}
extension Int : P {}
extension String : P {}

var p: P = 3
p = "str"
```

一方、`<T: P> T`は、条件を満たす**特定の**ある型を表しています。実行時の実際の値の型が
何になっているかはわかりませんが、コンパイル時の型検査では`T`という表現で特定の型を表しています。そのため、下記のように特定の型を代入する事はできません。

```swift
func g<T: P>(_ x: T) {
    var x = x
    x = "" // Cannot assign value of type 'String' to type 'T'
}
```

このように、existentialと型変数は、実行時に代入できる型の条件は同じでも、コンパイル時の型検査においては、型の同一性が失われている、維持しているのかという違いがあります。

SwiftのCore TeamメンバーのJoe Groffは、この違いを`value-level abstraction`(Existential)と`type-level abstraction`(型変数)と[説明しています。](https://forums.swift.org/t/improving-the-ui-of-generics/22814)

# Existentialのopen

ここで、Existentialから型変数を取り出す事を考えることができます。つまり、コードにおいて、Existentialに入っている型の同一性を復旧させます。

## 例1

この概念は、[Generics Manifestoで紹介されています。](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#opening-existentials)

```swift
let e1: Equatable = ...
let e2: Equatable = ...

if let storedInE1 = e1 openas T { // T is the type of storedInE1, a copy of the value stored in e1
  if let storedInE2 = e2 as? T {  // Does e2 have type T? If so, copy its value to storedInE2
    if storedInE1 == storedInE2 { ... } // Okay: storedInT1 and storedInE2 are both of type T, which we know is Equatable
  }
}
```

この例は、`Equatable`のexistentialである`e1`と`e2`を`==`で比較する架空のコードです。実際の現在のSwiftにおいては、`Equatable`のexistentialはありませんが、それが仮にあった場合の話です。

このコードでは、`openas`という架空の言語機能を使って、`e1`から真の型`T`を取り出し、`e2`を、その`T`にキャストした後で、`e1`と`e2`の`==`比較をしています。この、真の型を取り出す操作をExistentialのopenと言います。

なぜこのような手間を踏む必要があるかというと、`Equatable`プロトコルは自身と同じ型を比較するためのプロトコルだからです。その機能を提供する`==`メソッドの型は`func ==(lhs: Self, rhs: Self) -> Bool`であり、両辺が同じ型でなければ呼び出せません。`Equatable`のexistentialである`e1`と`e2`にはそれぞれ異なる型が入っているかもしれないので、そのままでは比較できないのです。

ちなみに、本題とは関係ありませんが、標準ライブラリには`AnyHashable`というtype erasureが提供されていて、これを使うと異なる型の等値比較を行うことができます。

```swift
var e1 = AnyHashable(Int(3))
var e2 = AnyHashable(Double(3.0))
print(e1 == e2) // true
```

## 例2

[Joe Groffがフォーラムに投稿した書き込みにもう一つの例があります。](https://forums.swift.org/t/improving-the-ui-of-generics/22814)

```swift
func bar(x: Collection) { 
	let <X: Collection> openedX = x // X is now bound to the dynamic type of x
	let start = openedX.startIndex
	let first = openedX[start] // OK, indexing X with a value of type X.Index, to get a result of type X.Element
}
```

この例では、また別の架空の構文を使って、`Collection`のexistentialである`x`から、真の型を`X`として取り出しています。これにより、`openedX`のsubscriptに`openedX`の`startIndex`を渡しています。

なぜこのような処理が必要かというと、`Collection`は自身の`Index`型を持っているが、existentialのままではその型情報が消えてしまうために、型安全なコードが書けないからです。

例えば標準ライブラリには`AnyCollection`というtype erasureがありますが、これを使うと下記のようなコードを書くことができます。このコードはコンパイルすることはできますが、実行時にクラッシュしてしまいます。それは、真の型`Array<Int>`のsubscriptに対して、`String.Index`を渡しているからです。

```swift
var c1 = AnyCollection([3])
var c2 = AnyCollection("str")
print(c1[c2.startIndex])
```

仮に`Collection`のexistentialがあったとしても、これと同様に、真の型がわからないので、真の型に対応した`Index`型もわからず、型安全にできないのです。

# Protocol Extensionによるopen

ここまで見たように、Existentialのopenという概念は公式に説明されていますが、実際の言語仕様としては導入されていません。しかし実は、Protocol Extensionにおいては、すでにExistentialのopenが実現されているのです。

```swift
protocol P {
    func f()
}

extension Int : P {}

extension P {
    func f() {
    }
}
```

このように、プロトコル`P`のメソッド`f`が、Protocol Extensionによって実装を与えられている状況を考えます。

このとき、`P`のexistential型に対しても、`f`を呼び出すことができます。

```
var x: P = 3
x.f()
```

実はここでExistentialがopenされています。これは、`P.f`における`self`の型を考える事でわかります。

`P.f`の型は何になっているのでしょうか。もし、`P`のexistentialであったとしたら、前述の例と同様にして、`self`の型の変数に対して、何か`P`を満たす型を代入できるはずです。

```swift
extension P {
    func f() {
        var x = self
        x = Int(3) // Cannot assign value of type 'Int' to type 'Self'
    }
}
```

しかし、代入することはできません。一方で、`self`は`P`を満たしているとして扱えます。下記のように他にもメソッド`g`があったとき、それを呼び出すことができます。

```swift
protocol P {
    func f()
    func g()
}

extension P {
    func f() {
        g()
    }
}
```

`self`はこのような特性をもっているので、`<T: P> T`という型になっていると考えられます。

もう一つの根拠として、このコードのSILコードがあります。

```c
// P.f()
sil hidden @$s1a1PPAAE1fyyF : $@convention(method) <Self where Self : P> (@in_guaranteed Self) -> ()
```

`P.f`はこのように、`<Self where Self : P>`というジェネリックシグネチャを持っていて、引数の型が`Self`になっています。

そして、このextensionによって自動実装された`Int.f`のSILは下記のようになります。

```c
// protocol witness for P.f() in conformance Int
sil private [transparent] [thunk] @$sSi1a1PA2aBP1fyyFTW : $@convention(witness_method: P) (@in_guaranteed Int) -> () {
// %0                                             // user: %2
bb0(%0 : $*Int):
  // function_ref P.f()
  %1 = function_ref @$s1a1PPAAE1fyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %2
  %2 = apply %1<Int>(%0) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  %3 = tuple ()                                   // user: %4
  return %3 : $()                                 // id: %4
} // end sil function '$sSi1a1PA2aBP1fyyFTW'
```

これを見ると、`P.f`である`$s1a1PPAAE1fyyF`に対して、型変数`<Self>`に`Int`を与えて呼び出している事が確認できます。これは通常のジェネリックメソッドと同じ機構になっています。

このように、`self`の型が`<Self : P> Self`という型変数になっているのは、実はextensionに限らずプロトコルのメソッドの共通の特徴です。

# Self conformanceとopen

protocolのexistentialは自分自身のprotocolに準拠しません。[詳細は以前書いた記事を参照してください。](https://qiita.com/omochimetaru/items/06c7c9ed34695b94247e)

これは記述でいうと、`P`は`<T: P> T`にマッチしないということです。一方で、ここまでで見たように、protocol extensionのメソッドが呼び出される時、`P`型である`self`が`<Self: P> Self`型としてopenされています。この違いを説明します。

まず、なぜexistentialをopenできるのかというと、`P`型の変数には、実際に`P`を満たす真の型を持った値が入っているからです。`<Self: P> Self`としてopenされているのは、この真の値が対象なのです。この値の型が`P`を満たしているという事は、`P`の存在型に値を代入する際の条件でした。よって、それがopenできるのは当然です。 

一方、protocolのself conformanceとして議論しているのは、`P`のexistential型が、プロトコル`P`に準拠できるか、ということです。これは特定の値とは関係の無い、型の話です。

ここで逆に、`P`型がself-conformanceを満たす条件を考えてみます。それは`@objc`がついていて、スタティックメンバを持たないことです。スタティックメンバを持たないということは、その`P`型に対して何か関わるときは、かならずインスタンスが絡んでいるということです。そして、インスタンスが絡んでいるということは、内部に`self`を保持したexistential型の値が対象になっているということです。これはexistentialがopenできる理由と同じ事を考えているとわかります。

# protocol extensionによるself conformanceの制約の回避

これまでの話で、protocol extensionを使うとexistentialがopenしていることがわかりました。これを利用すると、self-conformanceがなくて困った場合に、問題を回避できる場合があります。

例えば、下記のように、`P`型が`Encodable`であるときに、`P`のexistentialを持つ型`S`があったとします。

```swift
protocol P : Encodable {}
extension Int : P {}
struct S {
    var p: P
    init() { p = 3 }
}
```

`S`の値があるとき、`p`を`encode`しようと思っても、`JSONEncoder`は`<X: Encodable> X`は受け取れますが、`Encodable`のexistentialはサポートしていないため、できません。

```swift
func main(_ s: S) throws {
    let encoder = JSONEncoder()
    try encoder.encode(s.p) // Protocol type 'P' cannot conform to 'Encodable' because only concrete types can conform to protocols
}
```

そこで、`P`に下記のようなextensionを与えて、それ経由の呼び出しに書き換えると、encodeができます。

```swift
extension P {
    func encode(jsonEncoder: JSONEncoder) throws -> Data {
        return try jsonEncoder.encode(self)
    }
}

func main(_ s: S) throws {
    let encoder = JSONEncoder()
    try s.p.encode(jsonEncoder: encoder)
}
```

# 合わせて読みたい

[型システムの理論からみるSwiftの存在型(Existential Type)](https://qiita.com/ukitaka/items/a993b5d7ed5ae84b1b52)
[Swiftでprotocol型の値がそのprotocol自身にconformしていない理由](https://qiita.com/ukitaka/items/8bcff4348c79d820ba32)