Swift 4.1で導入されたConditional Conformanceのインパクト

Swift 4.1 のおける最も大きな変更の一つは **[Conditional Conformance](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md)** [^1]のサポートです。 Conditional Conformance はジェネリクス関連の言語仕様の一つです。

本投稿では、 Conditional Conformance とは何か、何がうれしいのか、どのようなことができるようになったのかということを説明します。

## Conditional Conformance とは

Swift 4.0 まででも、次のようなコードは正しく実行できました。

```swift
let a = [2, 3, 5]
let b = [2, 3, 5]
print(a == b) // true
```

しかし、 Swift 4.0 ではこちらのコードはコンパイルエラーになります。

```swift
// Swift 4.0
let a = [[2, 3], [5]]
let b = [[2, 3], [5]]
print(a == b) // コンパイルエラー
```

最初のコードでは `a` と `b` の型は `[Int]` （ `Array<Int>` ）ですが、その次のコードでは `[[Int]]` （ `Array<Array<Int>>` ）です。つまり、 Swift 4.0 までは `[Int]` 同士を `==` で比較することはできましたが、 `[[Int]]` 同士を `==` で比較することはできなかったということです。

Swift 4.1 からは `[[Int]]` 同士でも `==` で比較することができるようになりますが、これを実現しているのが Conditional Conformance です。

### `==` という関数

この挙動を理解するには、まず Swift の `==` が何なのかを理解する必要があります。 Swift では、 `==` を含め演算子の正体は関数です。たとえば、次のような関数を実装すると、自作の `Foo` に対して `==` 演算子が使えるようになります。

```swift
struct Foo { let value: Int }

func ==(lhs: Foo, rhs: Foo) -> Bool {
    return lhs.value == rhs.value
}
```

自作した `==` はこのように使うことができます。

```swift
Foo(value: 42) == Foo(value: 42) // true
Foo(value: 42) == Foo(value: -1) // false
```

## `Equatable` とは

さて、 Swift にはこれとは別に `Equatable` というプロトコルがあります。 `Equatable` に適合する（ conform する）型は `==` を使えると知っている人も多いんじゃないかと思います。 `Int` や `String`, `Bool` などは `==` で比較することができますが、これらの型はすべて `Equatable` に適合しています。

```swift
extension Int : Equatable {
    static func ==(lhs: Int, rhs: Int) -> Bool {
        ...
    }
}
```

しかし、さっき作った `Foo` は `Equatable` ではありませんでしたが、 `==` で比較することができました。一見、 `Equatable` なんてなくても問題ないように思えます。 `Equatable` だと何がうれしいのでしょうか？

たとえば、指定されたのと同じ値が `Array` の中にいくつあるかを数える関数 `countElements` を実装することを考えてみましょう。次のように使えるイメージです。

```swift
let a = [0, 1, 1, 2, 3, 5, 8]
countElements(in: a, equalTo: 1) // 2
```

この `countElements` は次のように実装することができます。

```swift
func countElements(in array: [Int], equalTo value: Int) -> Int {
    return array.reduce(0) { $0 + ($1 == value ? 1 : 0) }
}
```

しかし、これでは `countElements` を `[Int]` 以外の型に使うことができません。

```swift
let b = ["s", "w", "i", "f", "t", "t", "w", "e", "e", "t", "s"]
countElements(in: b, equalTo: "s") // [Int] でないのでコンパイルエラー
```

もちろん、 `[String]` を引数にとる `countElements` を実装すればできるようになりますが、 `[Double]` や `[Bool]` などを考えるとキリがありません。

今、 `array` の要素に対して行っている操作は `==` だけなので、 `==` を使える型を要素に持った `Array` に対して一般的に `countElements` を実装できれば問題は解決します。そして、 `==` で比較可能な型であるということを表すのが `Equatable` プロトコルです。

`Equatable` な要素を持つ `Array` 一般について `countElements` を実装すると次のようになります。 `T : Equatable` なので `T` を `==` で比較できます。

```swift
func countElements<T : Equatable>(in array: [T], equalTo value: T) -> Int {
    return array.reduce(0) { $0 + ($1 == value ? 1 : 0) }
}
```

この `countElements` を使えば、先程はコンパイルエラーになった次のコードも正しく実行できます。

```swift
let b = ["s", "w", "i", "f", "t", "t", "w", "e", "e", "t", "s"]
countElements(in: b, equalTo: "s") // 2
```

しかし、自作した `Foo` は `Equatable` でないので、 `[Foo]` に対しては `countElements` を使うことができません。

```swift
let c = [Foo(value: 1), Foo(value: 1), Foo(value: 2), Foo(value: 3)]
countElements(in: c, equalTo: Foo(value: 1)) // コンパイルエラー
```

`Foo` を `Equatable` に適合させるには次のように実装します。

```swift
struct Foo { let value: Int }

extension Foo : Equatable {
    static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs.value == rhs.value
    }
}
```

そうすれば `Foo` も `Equatable` なので、 `[Foo]` に対しても `countElements` を使えるようになります。

```swift
let c = [Foo(value: 1), Foo(value: 1), Foo(value: 2), Foo(value: 3)]
countElements(in: c, equalTo: Foo(value: 1)) // 2
```

これが Swift における `==` の仕組みと `Equatable` の役割です。

## `[Int]` は `Equatable` ？

では冒頭の、 `[Int]` は `==` で比較できるけど `[[Int]]` は `==` で比較できないのはなぜか、という問に戻りましょう。そもそも、 **Swift の `Array` は `Equatable` なのでしょうか？それとも `Equatable` ではないのでしょうか？**

たとえば、次のコードのように `[Int]` 同士は比較したいので `Array` には `Equatable` であってほしいように思います。

```swift
let a = [2, 3, 5]
let b = [2, 3, 5]
print(a == b) // true
```

しかし、 `Equatable` でない要素を持つ `Array` を考えることもできます。

そもそも、 `Equatable` でない型とはどんな型でしょうか。たとえば、多くのエラー型では equality を定義するのが困難です。エラーが equal であるとはどういう状況でしょうか？発生した状況や行、スタックトレースが完全に等しければ、異なるタイミングで発生したエラーでも等しいのでしょうか？

クラスであればインスタンスが同一（ `===` が `true` ）であることを equality の定義とすることもできるかもしれませんが、標準ライブラリの `DecodingError` のように値型のエラーもあります。値型では `===` は使えません。無理やり `DecodingError` を `Equatable` に適合させようとして、保持する値がすべて等しかったときに等しいことにしたとしても、別のタイミングで発生したたまたま同じに見えるエラーを同値とすることに何か意味があるでしょうか？

このように、 equality を定義すること自体に意味が見出だせない型も存在します。そのような型では無理に `==` の意味を定義するより、比較しようとしたらコンパイルエラーになるのが自然でしょう。

```swift
let a: DecodingError = ...
let b: DecodingError = ...
print(a == b) // コンパイルエラー
```

Objective-C や Java などの昔ながらのオブジェクト指向言語では、 `isEqual` や `hash` が `NSObject` のようなルートクラスに定義されており、必要に応じてこれをオーバーライドするようになっています。しかし、それでは本来 `isEqual` や `hash` を持つべきでない型もそれらを使えることになってしまいます。静的型付言語なのに `isEqual` や `hash` を持つべきかを型で表すことができず、適切にコンパイルエラーを出せないというのは敗北です。

そういう意味で、 `Equatable` や `Hashable` で明示的に `==` や `hashValue` が定義されるべきかを表現することができる Swift は、 Objective-C 等の言語と比べてより成熟した静的型付言語だと言えるでしょう。

`DecodingError` は `Equatable` でないと言いました。そうであれば `DecodingError` の `Array` である `[DecodingError]` も当然 `Equatable` でないと考えるのが自然です。要素が等しいかどうかを判定できないのに、その `Array` が等しいかどうかをどのように定義することができるでしょうか。

```swift
let a: [DecodingError] = ...
let b: [DecodingError] = ...
print(a == b) // コンパイルエラー
```

つまり、要素が `Equatable` なら `Array` も `Equatable` に、要素が `Equatable`  **でなければ** `Array` 自体も `Equatable` **でなく** なってほしいということです。

### Conditional Conformance

> 要素が `Equatable` なら `Array` も `Equatable` に、要素が `Equatable`  **でなければ** `Array` 自体も `Equatable` **でなく** なってほしい

それをコードで表すと次のように書けます。 **これが Conditional Conformance です。**

```swift
extension Array : Equatable where Element : Equatable {
    ...
}
```

この例だと `Element` が `Equatable` かという条件（ condition ）によって `Array` が `Equatable` に適合（ conform ）するかを決定します。なので Conditional Conformance と呼びます。

しかし、 Swift 4.0 までは Conditional Conformance がサポートされていませんでした。だからといって、 `[Int]` 同士を `==` で比較できないというのはあまりにも不便すぎます。そのため、 `Array` は `Equatable` ではありませんが、次のような `==` 関数を別途提供することで `[Int]` 同士の比較が実現されていました。

```swift
// Swift 4.0
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool {
    ...
}
```

そういうわけで、 `[Int]` は `Equatable` ではないにも関わらず、これまでも `==` で比較することができていたのです。

```swift
// Swift 4.0
let a = [2, 3, 5]
let b = [2, 3, 5]
print(a == b) // true
```

しかし、 `T : Equatable` に対して `[T]` 同士の `==` は標準ライブラリで提供されていますが、 `[[T]]` の比較は提供されていません。それが、冒頭のコードがエラーになった理由です。

```swift
// Swift 4.0
let a = [[2, 3], [5]]
let b = [[2, 3], [5]]
print(a == b) // コンパイルエラー
```

Swift 4.1 からは Conditional Conformance によって `[[Int]]` 同士を `==` で比較できるようになりました。

```swift
// Swift 4.1
let a = [[2, 3], [5]]
let b = [[2, 3], [5]]
print(a == b) // true
```

`[Int]` は `Equatable` な要素 `Int` を持つ `Array` なので `Equatable` です。同様に、 `[[Int]]` は `Equatable` な要素 `[Int]` を持つので `Equatable` です。

そのように再帰的に考えると、たとえ `[[[[[[[[[[[[[[[[[Int]]]]]]]]]]]]]]]]]` であっても `Equatable` になります。

```swift
// Swift 4.1
let a = [[[[[[[[[[[[[[[[[42]]]]]]]]]]]]]]]]]
let b = [[[[[[[[[[[[[[[[[42]]]]]]]]]]]]]]]]]
print(a == b) // true
```

これで、 Conditional Conformance のおかげで `[[Int]]` 等を `==` で比較できるようになったことがわかったと思います。しかし、それがそんなにうれしいことでしょうか？

## Conditional Conformance が標準ライブラリにもたらす変化

ここからは、 Conditional Conformance が Swift の標準ライブラリにもたらす変化をいくつか紹介します。

### `Optional`

まずは `Array` と似たところで `Optional` です。これまでの `Optional` は `Equatable` ではありませんでした。

たとえば、次のようなコードを書いた経験が一度はあると思いますが、 `Int?` （ `Optional<Int>` ）は `Equatable` ではないので、本来はこの `==` は使えません。

```swift
let a: Int? = ...
if a == nil { ... }
```

Swift 4.0 までは `Array` と同じように次のような `==` 関数でお茶を濁していました。

```swift
// Swift 4.0
func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool {
    ...
}
```

Swift 4.1 からは Conditional Conformance で `Int?` 等も `Equatable` になります。

```swift
// Swift 4.1
extension Optional : Equatable where Wrapped : Equatable {
    static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool {
        ...
    }
}
```

これによって、 `Int??` 同士の比較をすることもできるようになりました。

```swift
// Swift 4.1
let a: Int?? = 42
let b: Int?? = 42
print(a == b) // true
```

他にも、 `XCTAssertEqual` なんかにも影響があります。 `XCTAssertEqual` は `Equatable` な値を受け取ってチェックを行います。しかし、 `Optional` は `Equatable` でないのでそのままでは `XCTAssertEqual` を使えません。そのため、 `==` 同様 `Optional` を受け取る特別な `XCTAssertEqual` がオーバーロードされていました。

Swift 4.1 からは、 `Optional<T> : Equatable where T : Equatable` なので、 `XCTAssertEqual` 一つで済むようになります。

```swift
let a: Int = 42
let b: Int? = 42

// ↓の二つは Swift 4.0 では別の関数、
// Swift 4.1 では同じ関数
XCTAssertEqual(a, a)
XCTAssertEqual(b, b)
```

### `Codable`

これまでは `Array` や `Optional` のような具象型の、 `Equatable` の適合について見てきました。今度はプロトコル視点で、 `Codable` について見てみます。

`Codable` がおもしろいのは、 Conditional Conformance がない Swift 4.0 の時点ですでに `Array` や `Optional` が `Codable` になっていることです。

なので、 `[Int]` や `Int?` をエンコード／デコードすることができます。

```swift
// Swift 4.0
import Foundation

let a = [2, 3, 5]
try! JSONEncoder().encode(a) // OK
```

しかし、もし `Codable` でない要素を持つ `Array` をエンコード／デコードしようとするとどうなるのでしょうか？

```swift
// Swift 4.0
import Foundation

struct Foo {}

let a = [Foo(), Foo(), Foo()]
try! JSONEncoder().encode(a) // ???
```

`Array` は `Codable` なので当然コンパイルは通ります。

```swift
// Swift 4.0
import Foundation

struct Foo {}

let a = [Foo(), Foo(), Foo()]
try! JSONEncoder().encode(a) // コンパイル可
```

しかし、 `Foo` を `encode` することはできないので実行時エラーになります。これをコンパイルエラーにできないのは静的型付言語としてイケてないですね。

```swift
// Swift 4.0
import Foundation

struct Foo {}

let a = [Foo(), Foo(), Foo()]
try! JSONEncoder().encode(a) // 実行時エラー
```

Swift 4.1 では Conditional Conformance で、要素が `Codable` なときだけ `Array` も `Codable` になります。

```swift
// Swift 4.1
extension Array : Codable where Element : Codable {
    ...
}
```

その結果として、先程のコードは見事にコンパイルエラーになります。

```swift
// Swift 4.1
import Foundation

struct Foo {}

let a = [Foo(), Foo(), Foo()]
try! JSONEncoder().encode(a) // コンパイルエラー
```

おもしろいのは、 Swift 4.0 で、 `Equatable` は `Equatable` **でない** 側に、 `Codable` は `Codable` **である** 側に倒されているということです。

`==` については `[T]` だけ関数版でサポートしておけばお茶を濁すことができましたが、 `Codable` はそういうわけにはいきません。 JSON だと複雑なネスト構造を持ちますし、 `Codable` は JSON に限った話でもありません。 `==` のときのように `encode` をオーバーロードして済む話ではなく、ネストを扱うためには広く `Codable` として受け入れて、要素が `Codable` でない場合は実行時エラーで対処するしかなかったのでしょう。

Swift 4.1 からは Conditional Conformance のおかげでジェネリックな型を安全に `Codable` にすることができるようになりました。

### `Range`

Conditional Conformance がちょっと違った種類の影響を与えるものに `Range` ファミリーがあります。これは、 Conditional Conformance 関連で僕が一番待ち望んでいたものでもあります。

Swift の `Range` ファミリーはややこしいです。 Swift 4.0 ではざっと次のような型がありました。多すぎてぞっとしますね。

```swift
// Swift 4.0
let a: Range<Int>                     = 2..<5
let b: CountableRange<Int>            = 2..<5
let c: ClosedRange<Int>               = 2...5
let d: CountableClosedRange<Int>      = 2...5
let e: PartialRangeFrom<Int>          = 2...
let f: CountablePartialRangeFrom<Int> = 2...
let g: PartialRangeUpTo<Int>          =  ..<5
let h: PartialRangeThrough<Int>       =  ...5
let i: UnboundedRange                 = (...)
```

Conditional Conformance によってコメントアウトした型をなくすことができます。これで 1 / 3 の型を減らせます。

```swift
// Swift 4.1
let a: Range<Int>                        = 2..<5
// let b: CountableRange<Int>            = 2..<5
let c: ClosedRange<Int>                  = 2...5
// let d: CountableClosedRange<Int>      = 2...5
let e: PartialRangeFrom<Int>             = 2...
// let f: CountablePartialRangeFrom<Int> = 2...
let g: PartialRangeUpTo<Int>             =  ..<5
let h: PartialRangeThrough<Int>          =  ...5
let i: UnboundedRange                    = (...)
```

よく見ると、なくすことができる型は右辺が他と重複しています。たとえば `Range<Int>` と `CountableRange<Int>` は両方とも右辺が `2..<5` です。では `Range` と `CountableRange` の違いは何でしょうか？

`Range` は単に `lowerBound` から `upperBound` までの範囲を表すだけですが、 `CountableRange` はその間の値を数え上げることができる型です。たとえば、 `2..<5` という `CountableRange` は `2, 3, 4` です。イテレートして `for` 文でループすることもできます。

```swift
// Swift 4.0
let a: CountableRange<Int> = 2..<5

for x in a {
    print(x) // 2, 3, 4 が順に表示される
}
```

しかし、同じ `2..<5` でも `Range` ではこのようなことはできません。 `Range` はあくまで範囲を表すだけだからです。

```swift
// Swift 4.0
let a: Range<Int> = 2..<5

for x in a {
    print(x) // コンパイルエラー
}
```

これは、 `Range<Double>` が数え上げられないのと同じです。 `2.0...<5.0` に存在する `Double` をすべて列挙するのは、実数という概念的には不可能ですし、有限個の浮動小数点数と考えても非現実的です。 `Range<Int>` はたまたま数え上げられる `Int` 型の範囲を表しているだけで、 `Range<Double>` と同様に範囲を表しているだけに過ぎません。

範囲を表すだけであれば、数値でなくても値の順序関係さえ決められれば（ `Comparable` なら） OK です。たとえば、 `Range<String>` を考えることもできます。

```swift
let a: Range<String> = "cat" ..< "dog"

// 辞書順で "cat" から "dog" の間に存在するか
a.contains("cow")   // true
a.contains("sheep") // false
```

`Range<String>` で文字列の範囲を考えることには意味がありますが、 `"cat" ..< "dog"` の範囲の文字列をすべて列挙することはできません。その特性の違いを表現するために `CountableRange` が存在しているのです。

加えて、 Swift の `Range` には `upperBound` を含むか、含まないか、存在しない（ `lowerBound` だけが存在する）かというバリエーションがあります。これと `Countable` を組み合わせると型がかけ算で増えてしまいます。そのかけ算の結果が先程の `Range` ファミリーの一覧です。しかし、本当にこのバリエーションは必要なのでしょうか？

よくよく考えてみると、 `Range<Bound>` が `Countable` かどうかという性質は `Bound` の型によって決まるはずです。 `Int` なら列挙できますが、 `Double` や `String` だと列挙できません。そうだとすると、わざわざ `Range` と `CountableRange` という別の型を作らなくても、型パラメータが `Int` なら列挙可、 `Double` や `String` なら列挙不可と、型パラメータによって挙動が変わってくれた方が便利です。

つまり、 `Range<Int>` だと `for` ループでイテレートできるけど `Range<String>` ではできないというようなことを実現したいということです。

```swift
// これを実現したい
let a: Range<Int> = 2..<5
for x in a { // ✅ OK
    ...
}

let b: Range<String> = "cat" ..< "dog"
for y in b { // ⛔ NG
    ...
}
```

`for` ループでイテレートできるためには `Sequence` プロトコルに適合しなければなりません。 `Range` の型パラメータが離散的で有限のものの場合には `Range` に `Sequence` に適合してほしい、これはまさに Conditional Conformance です。

Conditional Conformance でそれを実現するための PR が[こちら](https://github.com/apple/swift/pull/13342)です。

[ここ](https://github.com/airspeedswift/swift/blob/d7f73fcfd6519399ae5f550517ab5cadc67e7f13/stdlib/public/core/Range.swift#L157-L243)で Conditional Conformance を使って `Range` を `CountableRange` 相当のものにしています。

```swift
extension Range: Sequence
where Bound: Strideable, Bound.Stride : SignedInteger {
    ...
}
...
extension Range: Collection, BidirectionalCollection, RandomAccessCollection
where Bound : Strideable, Bound.Stride : SignedInteger
{
    ...
}
```

残念ながら、この PR による変更は Swift 4.1 には含まれていません。 **Swift 4.2 から** 使えるようになります。

これによって `CountableRange` などの冗長な型は取り除かれ、 `Range` だけで↓が実現できるようになります。

```swift
// Swift 4.2
let a: Range<Int> = 2..<5
for x in a { // ✅ OK
    ...
}

let b: Range<String> = "cat" ..< "dog"
for y in b { // ⛔ NG
    ...
}
```

もう、 `Range<Int>` を `CountableRange<Int>` に変換するボイラープレートに悩まされることもありません。

```swift
let a: Range<Int> = ...

// Swift 4.0
for x in CountableRange(a) { ... }

// Swift 4.2
for x in a { ... }
```

僕がこれを待ち望んでいたのは、 `Range` ファミリーを引数にとる関数やメソッドの実装が楽になるからです。

僕が作っている EasyImagy https://github.com/koher/EasyImagy というライブラリでは、 `subscript` を使って簡単に画像のピクセルにアクセスできるんですが、

```swift
let image = Image<RGBA<UInt8>>(named: ...)!

let pixel = image[x, y]
print(pixel.red)
```

`array[2..<5]` のようにして `ArraySlice` を得られるように、 `subscript` と `Range` を組み合わせて簡単に画像の一部を切り出せます。

```swift
// x が 100 から 200 まで、 y が 300 から 400 までの
// 100x100 の大きさの部分画像を取得
let slice = image[100..<200, 300..<400]
```

しかし、この `subscript` にあらゆる種類の `Range` を渡せるようにしようとすると、すべての `Range` 型の組み合わせに対応する必要があります。その結果、[こんなこと](https://github.com/koher/EasyImagy/blob/0.3.0/Sources/EasyImagy/RangedSubscript.swift)になってしまいました。

このコードは Swift 3 の頃のものなので `PartialRange` には対応していません。しかし、それでも 4 * 4 = 16 種類もの `subscript` を実装しています。もし、この頃に Conditional Conformance があれば、 `Countable` 系の `Range` が消えて、 2 * 2 = 4 種類の `subscript` で済んだはずです。

結局これについては Swift 4 の `RangeExpression` プロトコルで[抽象的に実装できるようになった](https://github.com/koher/EasyImagy/blob/e2d9fecc80a44e5d89b8e66d839620a9f7a55090/Sources/EasyImagy/ImageProtocol.swift#L35-L49)ので、 Conditional Conformance の恩恵を受けることはなくなりました。

しかし、冗長気味だった `Range` ファミリーが Conditional Conformance で整理されるのは喜ばしいことです。

## 自作の型と Conditional Conformance

Conditional Conformance は、何も標準ライブラリを改善するためだけのものではありません。自作の型を作るときにも役立ちます。先程紹介した EasyImagy を例に説明します。

2x2 のグレースケール画像を縮小して 1x1 にすることを考えてみましょう。

```swift
// グレースケール画像は各ピクセルが 0 から 255 までの値を取るので
// Image<UInt8> として UInt8 でピクセル一つを表す
let image = Image<UInt8>(width: 2, height: 2, pixels: [
    10, 20,
    30, 40,
])
```

各ピクセルの平均をとって `(10 + 20 + 30 + 40) / 4` 、つまり `25` という輝度値を持った 1x1 の画像が出来上がるはずです。

```swift
let resized = image.resizedTo(width: 1, height: 1)
print(resized[0, 0]) // 25
```

平均を取るためには足し算と割り算ができる必要があります。しかし、 `Image<Pixel>` の型パラメータ `Pixel` には任意の型を入れられるので、いつでも平均を計算できるとは限りません。

もし `Image<Pixel : Numeric>` と限定してしまえばいつでもピクセル値の平均を計算できることになります。しかし、そうすると `Image<Character>` でアスキーアートのような画像を表したり、 `Image<UInt8?>` で欠損のある画像を表現するようなことができなくなってしまいます。

そんなときでも Swift なら extension で解決できます。 `Numeric` な `Pixel` を持つ `Image` に対してのみ `resizedTo` を実装すればよいのです。

```swift
extension Image where Pixel : Numeric {
    func resizedTo(width: Int, height: Int) -> Image<Pixel> {
        ...
    }
}
```

さて、グレースケール画像であれば `Image<UInt8>` でよいですが、カラー画像を扱うには `Image<RGBA<UInt8>>` のように型パラメータがネストした状態になります。この `RGBA` も `Numeric` でないと平均が計算できません。

`Image<Pixel>` の `Pixel` が任意の型をとれるように、 `RGBA<Channel>` の `Channel` にも任意を型を設定できるようにしたいところです。そうすると、 `Channel` が `Numeric` のときだけ `RGBA` も `Numeric` にしたい、そんな気持ちになってきます。

そう、そういうときは Conditional Conformance です！

```swift
extension RGBA : Numeric where Channel : Numeric {
    ...
}
```

これによって、平均が計算できる `Image<RGBA<UInt8>>` はリサイズできるけど、 `Image<RGBA<Bool>>` はリサイズできないということが、コンパイル時にチェックできるようになります。

```swift
let a: Image<RGBA<UInt8>> = ...
let resizedA = a.resizedTo(width: w, height: h) // ✅ OK

let b: Image<RGBA<Bool>> = ...
let resizedB = b.resizedTo(width: w, height: h) // ⛔ NG
```

リサイズだけでなく、回転や補間、畳込みでも同様のことが言えます。

Conditional Conformance がなかった頃にこれに近いことを実現しようとすると大変でした。これまでは、よくあるピクセル型についてそれぞれ `resizedTo` 等のメソッドをオーバーロードして実装していました。

```swift
// よく使う Pixel 型に対してひたすらオーバーロード😂
extension Image where Pixel = UInt8 { func resizedTo(...) ... }
extension Image where Pixel = UInt16 { func resizedTo(...) ... }
extension Image where Pixel = UInt32 { func resizedTo(...) ... }
...
extension Image where Pixel = RGBA<UInt8> { func resizedTo(...) ... }
extension Image where Pixel = RGBA<UInt16> { func resizedTo(...) ... }
extension Image where Pixel = RGBA<UInt32> { func resizedTo(...) ... }
...
```

当然、こんなことを手作業でやってられないので、 [gyb](https://qiita.com/omochimetaru/items/422ddd04e95c55dd3833#gyb) （ Swift コンパイラで使われているツール）でコード生成していました。

```swift
% for pixel_type in pixel_types:
extension Image where Pixel = ${pixel_type} { func resizedTo(...) ... }
% end
```

しかし、 Swift 4.1 で念願の Conditional Conformance を手に入れて修正したのが[こちらの PR](https://github.com/koher/EasyImagy/pull/22) です。「 +997 −2,852 」です！

<img width="604" alt="easy-imagy-conditional-conformance-pr.png" src="https://qiita-image-store.s3.amazonaws.com/0/47085/3fd39143-4ac2-efa9-0da7-965586a99098.png">


しかも、 + のそこそこ大きな割合を specialize やファイル間のコードの移動が占めているので、実質的には関連コードの 8 割くらいを削減できたんじゃないかと思います。

```swift
// specialize の例
@_specialize(exported: true, where Self == Image<UInt8>, Kernel == Image<Int>)
@_specialize(exported: true, where Self == Image<UInt8>, Kernel == ImageSlice<Int>)
@_specialize(exported: true, where Self == ImageSlice<UInt8>, Kernel == Image<Int>)
@_specialize(exported: true, where Self == ImageSlice<UInt8>, Kernel == ImageSlice<Int>)
@_specialize(exported: true, where Self == Image<UInt16>, Kernel == Image<Int>)
@_specialize(exported: true, where Self == Image<UInt16>, Kernel == ImageSlice<Int>)
@_specialize(exported: true, where Self == ImageSlice<UInt16>, Kernel == Image<Int>)
@_specialize(exported: true, where Self == ImageSlice<UInt16>, Kernel == ImageSlice<Int>)
...
```

これはうれしい！！

そんなわけで、自作の型を実装してるときも Conditional Conformance 最高です。

## まとめ

- Swift 4.1 から Conditional Conformance が使えるようになった
- 標準ライブラリが便利になった
    - `Foo : Equatable` のとき、 `[Foo]` が `Equatable` になった
    - `Foo : Codable` **でない** とき、 `[Foo]` が `Codable` でなくなった
    - Swift 4.2 から `CountableRange` 等が `Range` 等に統合される
- 自作の型でも Conditional Conformance は活躍
    - コード生成でがんばってた箇所のコードを 8 割ほど削減できた

[^1]: Proposal では "Implemented (Swift 4.2)" となっていますが、これは Proposal の内容がフルにサポートされるのが 4.2 からということで、 Conditional Conformance 自体は 4.1 から利用できます。